spotifyovercastrssapple-podcasts

Extending Elm

We discuss what Elm is intended for, techniques for going beyond that, and how to make tools nice to use when you do.
June 29, 2020
#7

What can you do with Elm?

  • Html
  • Http
  • Ports
  • Web Components

Different techniques for extending elm

elm-hot and elm-hot-webpack-loader

Pitfalls and considerations

Codegen

  • Have a single clear source of truth for codegen
  • Prevent bad states with airtight abstractions, rather than having lots of caveats
  • Make sure public APIs for generated code look nice
  • Use doc comments

Macros

  • Elm code that doesn’t look like elm code
  • Tooling doesn’t work then - see Babel ecosystem
  • Violates Open close principle - you’re modifying the language, not extending it

Provide a platform with extensions in mind when you build tools so you don’t require users to hack

When you build a tool, think about the mental model for uses, let that guide you. Avoid leaky abstractions

Be opinionated about the core things, and unopionated about what’s not essential to the tool

  • [elm-spa](https://github.com/ryannhg/elm-spa)

Transcript

[00:00:00]
Hello, Jeroen.
[00:00:01]
Hello, Dillon.
[00:00:02]
How are you doing today?
[00:00:03]
As usual, pretty good.
[00:00:04]
How about you?
[00:00:05]
I'm good and I'm ready to dig into some Elm.
[00:00:11]
So today we have a question from a listener whose name is John Doe, so we actually don't
[00:00:17]
know if it's an anonymous user or a real name.
[00:00:21]
That's the tricky thing because it's an optional field in our question form, but they wrote
[00:00:27]
in John Doe by hand.
[00:00:29]
And there are a few John Does on the Elm Slack, so it could be anonymous too, but we will
[00:00:37]
never know.
[00:00:38]
That's right.
[00:00:39]
And for reference, if you haven't seen it already, elm.radio.com slash question, or
[00:00:46]
you can just go to elmradio.com and hit the submit a question button and we actually look
[00:00:51]
at them, believe it or not.
[00:00:53]
Yeah.
[00:00:54]
So do you want to read the question?
[00:00:56]
Yes.
[00:00:57]
So John Doe asks, well, he says very nice podcast.
[00:01:01]
Thank you, John Doe.
[00:01:02]
Thank you.
[00:01:03]
You both have a very nice synergy going on that makes episodes interesting and easy to
[00:01:07]
listen to.
[00:01:08]
Why thank you.
[00:01:09]
I'd like to suggest you both talk about extending Elm's capabilities beyond what it was thought
[00:01:16]
for.
[00:01:17]
When is it a good idea to extend Elm's capabilities?
[00:01:20]
Elm pages, Elm SPA, node scripting server, any other ideas?
[00:01:25]
Thanks again for putting out so much cool content.
[00:01:27]
Thank you for the question.
[00:01:29]
This is a juicy topic.
[00:01:30]
Yeah.
[00:01:31]
There's a lot to dig into.
[00:01:32]
It's going to be a long topic.
[00:01:34]
As usual.
[00:01:36]
Very interesting with a lot of ideas and opinions.
[00:01:40]
Yeah.
[00:01:42]
And I think this one, you know, I think it's interesting both from sort of a tinkerer and
[00:01:48]
tool builders perspective, i.e. people like you and me, Jeroen.
[00:01:53]
And I think it's interesting from a user's perspective too, because as a consumer of
[00:01:57]
these types of tools, there are a lot of questions about when is it overkill to use a tool when
[00:02:04]
maybe you could achieve a similar thing with a port or a web component?
[00:02:08]
How do you keep your code maintainable?
[00:02:11]
And like, so there are questions like that for somebody who's choosing to use tools like
[00:02:17]
this.
[00:02:18]
So I think it's good to have an understanding of when to reach for a particular type of
[00:02:23]
tool and technique for extending Elm.
[00:02:26]
Yeah.
[00:02:27]
So the first question is what can't you do with Elm?
[00:02:30]
Right.
[00:02:31]
So Elm is made for building web apps.
[00:02:34]
So anything that is a web app, you can do with it.
[00:02:37]
Anything that is not a web app, you probably can't do it.
[00:02:42]
Very simple.
[00:02:43]
Yes.
[00:02:44]
Although, of course, there's platform.worker.
[00:02:46]
And you know, technically, it's not exactly the intended use case for Elm.
[00:02:51]
But you can do a platform.worker.
[00:02:54]
You know, let's say you want to pass in some data from JavaScript land from node.js through
[00:03:01]
a flag or a port, and then have some computation and send it back to JavaScript and print it
[00:03:07]
out or whatever.
[00:03:08]
You know, that's not exactly an intended use case for Elm.
[00:03:11]
But it's going to work pretty smoothly.
[00:03:13]
You're not hacking Elm in a really crazy way and doing something that's far from the intended
[00:03:18]
use of it, I would say.
[00:03:20]
Yeah, it's just that you're using it in environments that it's not intended for.
[00:03:25]
Yes.
[00:03:26]
That's the difference, in my opinion.
[00:03:28]
Yes.
[00:03:29]
Yeah, maybe it's not first class support, but second class.
[00:03:33]
Second class sounds good.
[00:03:35]
Yeah.
[00:03:36]
Maybe to be explicit about what can't you do with Elm, maybe we should start with what
[00:03:41]
can you do with Elm.
[00:03:42]
So as you said, you can build web apps with it.
[00:03:45]
Obviously, there's first class support for putting HTML on the screen.
[00:03:49]
That's kind of the basic thing that you're doing.
[00:03:51]
Yeah.
[00:03:52]
And of course, there's first class support for grabbing data with HTTP requests.
[00:03:57]
There are certain parts of the web platform that are easily within reach.
[00:04:02]
If you have an href on an anchor tag, then when you click it, what do you know?
[00:04:08]
The web browser will go to that link.
[00:04:10]
And then of course, you can do commands.
[00:04:13]
So Elm has these managed effects, which it has a limited set of things that it allows
[00:04:20]
you to directly do that are side effects through your init and update functions.
[00:04:24]
Yeah, like getting time, getting random numbers.
[00:04:29]
Subscriptions also count as managed effects.
[00:04:33]
And then you got second class support for ports and web components.
[00:04:38]
So I think that's, again, I say second class because it's not the intended way, but it's
[00:04:45]
something you can use to get other things.
[00:04:49]
I mean, it is the intended way to extend Elm, but it's not directly built into it in a way.
[00:04:56]
It's not supported out of the box.
[00:04:59]
But the mechanisms, ports, flags, and web components are meant to be there.
[00:05:05]
Yeah.
[00:05:06]
Okay, so that's what you can do with Elm.
[00:05:09]
So what can't you do with Elm directly?
[00:05:14]
Anything that is not in that list.
[00:05:17]
There's so many things that you can't do that is just not listable.
[00:05:21]
Right.
[00:05:22]
Well, okay, so maybe let's go through some concrete examples.
[00:05:26]
And you and I are familiar with some of these having built tools to solve these problems
[00:05:33]
in ways that involve some of these techniques for extending Elm to do things you can't directly
[00:05:37]
do in pure Elm.
[00:05:38]
So, you know, one example that is front of mind for me is, for example, Elm doesn't give
[00:05:44]
you a way to do head text.
[00:05:45]
So if you want to add meta tags for SEO to your head, if you want to perform some HTTP
[00:05:53]
requests that get built into your bundle, you know, when you initially run your Elm
[00:05:57]
app, those things aren't supported out of the box in Elm.
[00:06:00]
There's no way to do that in pure Elm.
[00:06:03]
You know, you could you could add something in some Elm code somewhere that adds head
[00:06:07]
tags based on something you return from, you know, from a port upon init in your Elm app,
[00:06:14]
right?
[00:06:15]
That's something you could do.
[00:06:16]
And then you could pre render it.
[00:06:17]
Yeah.
[00:06:18]
And I mean, so technically, adding for let's take the example of adding head tags, adding
[00:06:23]
head tags is very much possible with Elm.
[00:06:26]
But it's not possible with pure Elm.
[00:06:28]
It's possible through a port.
[00:06:30]
And what happens when you're sending things through port?
[00:06:32]
Well, sometimes it's not going to be a first class experience.
[00:06:35]
Like we kind of talked about, you have to wire things up, you have to sort of manage
[00:06:40]
how you serialize and deserialize things when you're sending it to and from Elm.
[00:06:46]
You have to write some code in some other language, like JavaScript.
[00:06:50]
Very often.
[00:06:51]
Exactly.
[00:06:52]
And make sure that your contracts match up between the Elm side and the JavaScript side,
[00:06:55]
which is a tricky part.
[00:06:57]
It's tricky.
[00:06:58]
And when you're working with Elm, there's this elegance to it, because you know that
[00:07:02]
the pieces are going to fit together.
[00:07:04]
And the type system helps you with that.
[00:07:06]
And in that interop, when you when you say, okay, I want these head tags, then it's not
[00:07:14]
that same seamless experience, because you're not sure that it's gonna that the contract
[00:07:19]
on the other side is going to be met.
[00:07:21]
So that's one example of where a tool can help out is a tool can help with head tags
[00:07:27]
and say, I'm going to give you first class support for head tags.
[00:07:31]
And what it's basically saying is, I'm going to take on the responsibility for ensuring
[00:07:36]
that contract.
[00:07:37]
So I'm going to expose some Elm code to you as if Elm had head tags as a first class citizen.
[00:07:44]
And I'm taking on the responsibility of making sure that contract is met on the other side.
[00:07:49]
And handling some internals, you probably want it to pre render your HTML for you as
[00:07:55]
well, because that's another part of head tags is, if you add it after the JavaScript
[00:08:01]
takes control, then it doesn't always show up in SEO or give you the best experience
[00:08:06]
there.
[00:08:07]
So these are all things that you can sort of wrap something up in a way that a user
[00:08:12]
could do.
[00:08:13]
But you can wrap it up into a tool and give a nice API that to the user feels like they're
[00:08:20]
just calling these functions.
[00:08:22]
And as long as they call it in a way that makes the Elm compiler happy, it's going to
[00:08:27]
work as expected.
[00:08:28]
Yeah.
[00:08:29]
So we are talking about code in here, right?
[00:08:31]
Yeah.
[00:08:32]
So why don't we get into some of the techniques that you can actually use for extending Elm?
[00:08:37]
I think we've kind of given a sense of maybe why you might extend Elm.
[00:08:42]
There are things that maybe it's possible to do.
[00:08:46]
There's some things that maybe it's not even possible to do.
[00:08:50]
I kind of hinted at static HTTP before where, you know, in Elm pages, it's one of my favorite
[00:08:57]
topics.
[00:08:58]
But I think it's an interesting example because...
[00:09:00]
Go listen to episode one if you don't know what we're talking about.
[00:09:03]
That's right.
[00:09:04]
It gives you a way to extend what's possible to do with Elm in a way where there would
[00:09:09]
be so much manual work involved, so many, you know, things you'd have to wire up to
[00:09:13]
create your own custom script that call HTTP requests and make sure that matches up with
[00:09:19]
the way you're decoding that data from the Elm side.
[00:09:22]
It's so much work that you're probably not going to do it because you're either basically
[00:09:27]
building your own framework or you're very likely to run into errors where you're not
[00:09:32]
wiring something up correctly and things get out of sync.
[00:09:35]
And so this is an example of extending Elm to make it feel as if Elm can do something
[00:09:41]
that it can't out of the box.
[00:09:43]
Yeah.
[00:09:44]
What I think about it is extend the guarantees that the Elm compiler gives you.
[00:09:49]
I think that a lot of the tools that we use in the Elm ecosystem, they try to do that.
[00:09:55]
They try to give you guarantees that you do not have with ports or that you do not have
[00:10:02]
in some other ways and just try to make it feel like Elm.
[00:10:05]
Yes, exactly.
[00:10:06]
It tries to make it feel as if it's just part of Elm that's baked in and by wrapping the
[00:10:11]
internals in a nice API and having them work together to the user, it feels like that.
[00:10:17]
So that's sort of the goal for extending Elm.
[00:10:21]
I would say that's the goal is to make it feel like it's just a first class tool that
[00:10:25]
you're using.
[00:10:26]
I think that the tool that we have at the moment do reflect that philosophy of trying
[00:10:32]
to give the user very good user or developer experience.
[00:10:37]
In the safety of not having these caveats that, hey, this will work as long as you do
[00:10:43]
this, this, this and this and wire this up in this place in that way and your decoder
[00:10:49]
here matches your encoder that you're using here.
[00:10:51]
It's like, no, we want it so that if the Elm compiler tells you that everything matches
[00:10:55]
up, then you don't have to worry about anything else.
[00:10:58]
Just make the Elm compiler happy and you'll make the tool happy.
[00:11:01]
That's sort of the goal.
[00:11:03]
Okay.
[00:11:04]
So let's get into some of these techniques.
[00:11:06]
So how do you extend Elm?
[00:11:09]
What are even the possible ways to extend Elm in the way we've been talking about?
[00:11:14]
So one of them is code generation.
[00:11:16]
It's when you generate Elm code, write it to a file, and then the Elm compiler just
[00:11:21]
thinks it's any normal Elm file, just like one of the ones that you wrote, except that
[00:11:30]
it isn't and you don't manage it.
[00:11:34]
The code is just simple Elm code.
[00:11:37]
So as long as it's valid Elm code and the type checks and the types check out with everything
[00:11:44]
else, that's a very good solution to extend Elm, I think.
[00:11:49]
Right.
[00:11:50]
So you can hook into the Elm compiler in a way because you're just generating this code
[00:11:56]
that you can now consume this generated code and the Elm compiler will just treat it like
[00:12:02]
any other Elm code except that a machine built it from something.
[00:12:05]
So for example, Elm GraphQL takes your GraphQL schema, which is a set of types for your API,
[00:12:14]
and then it generates Elm code to allow you to consume that in a type safe way.
[00:12:18]
The only downside is that you need to have a generation step before you run the Elm compiler.
[00:12:23]
So you need to generate those Elm files before you run the compiler.
[00:12:27]
Right.
[00:12:28]
Otherwise it will break because the code isn't there.
[00:12:31]
Yes, that's a great point.
[00:12:33]
You add an extra step and you could potentially get out of sync depending on how you do it.
[00:12:39]
So you have to ensure, for example, if it's Elm GraphQL, you need to hook it into your
[00:12:45]
build process in a way where you're guaranteed to have a fresh copy of the generated code
[00:12:52]
when you compile your code.
[00:12:54]
So you can also do macros.
[00:12:56]
Now macros is generally a language feature and it's not a language feature that Elm has,
[00:13:03]
but you can emulate the experience of macros.
[00:13:05]
So I think first let's just define macros.
[00:13:09]
The way I think about a macro is a function takes a value.
[00:13:13]
That's what functions do.
[00:13:14]
They take values.
[00:13:15]
Those values may include functions, but they're just concrete values.
[00:13:19]
Whereas a macro takes an AST.
[00:13:21]
It takes code that is not yet evaluated as an argument, and then often you'll have the
[00:13:28]
ability to evaluate that code.
[00:13:30]
So I really like this example of this X unit testing framework in Elixir, which has this
[00:13:38]
assert macro.
[00:13:40]
It's not a function, it's a macro.
[00:13:41]
And what that means is that the macro has access to the AST.
[00:13:45]
So if you say assert one double equals some variable, then it can look at the left handside
[00:13:52]
and the right hand side of the double equals.
[00:13:54]
And actually it might not just be variable.
[00:13:57]
It can tell you the variable name, but it could be a complex expression.
[00:14:00]
You know, you could say, exactly.
[00:14:04]
And it can actually tell you what code it used in the error message.
[00:14:09]
Or if you say assert three greater than four, then it can tell you, hey, I expected the
[00:14:18]
thing on the left hand side, which was three, to be greater than four, but it wasn't greater
[00:14:22]
than four.
[00:14:23]
And it can give you a precise error message based on inspecting the AST.
[00:14:27]
It can do more sophisticated things without having to have a special assert greater than
[00:14:33]
helper.
[00:14:34]
Basically, the way I see it is a macro is something that will replace code by some other
[00:14:40]
pieces of code.
[00:14:41]
So when I learned C at university, you could define macros.
[00:14:48]
So define constants that were going to be inline in some places, kind of like variables
[00:14:55]
in a way, the global ones.
[00:14:57]
And you could do things more complicated than that.
[00:15:00]
And yet, this example is just very good too, because you have an assert expression.
[00:15:08]
And when you build it, or when you run tests, it gets replaced by some other piece of code.
[00:15:15]
So the Ava test framework in JavaScript does the same thing.
[00:15:18]
It has a Babel plugin that checks for T.is, which is the assert function, and it replaces
[00:15:27]
the functions to something that will make a much better error message.
[00:15:33]
So the exact same idea.
[00:15:35]
So you could use macros for other things, like in Babel land, so JavaScript land.
[00:15:42]
You could enable new syntaxes, for instance.
[00:15:47]
So that's what is happening a lot in the ES 2020, ES 2021, and where you try to enable
[00:15:55]
new features before Node or your web browser has it.
[00:16:01]
And that is something that we don't have in Elm, at least for now.
[00:16:05]
And I'm quite happy about that, because it's a dangerous one.
[00:16:10]
Right.
[00:16:11]
And I think we'll maybe dig into that more.
[00:16:13]
Maybe we should cover the rest of the techniques before then.
[00:16:18]
But we'll get into the trade offs between these, the maintainability cost versus the
[00:16:25]
conveniences of them.
[00:16:26]
OK, so let's kind of go through the rest of these techniques here.
[00:16:30]
So another technique for extending Elm, for having this experience of a first class set
[00:16:37]
of functionality that doesn't exist in Elm directly is I like to think of this one as
[00:16:43]
like wrapper Elm apps.
[00:16:45]
So Elm used to have this notion of an effect manager that you could sort of have this like
[00:16:51]
implicit state in your app and you could, you know, you could perform some sort of side
[00:16:57]
effect that would actually rely on state and perform a set of commands in order to actually
[00:17:05]
achieve some like higher level effect.
[00:17:07]
Right.
[00:17:08]
And the fact is that you don't need any magical Elm features to do that.
[00:17:13]
You can actually do that by wrapping your Elm app.
[00:17:16]
So you can, if you wanted to have a modal window pop up when, you know, when you give
[00:17:23]
a certain instruction to your sort of top level Elm app.
[00:17:28]
Well, if you have an Elm app that wraps your actual Elm app, you can you can have this
[00:17:34]
contract where you say, hey, if I pass this information here, then it's going to set the
[00:17:38]
top level state of here's the modal I'm showing, you know, or for example, like I want to show
[00:17:45]
a flash message for 10 seconds that shows that this operation succeeded or that shows
[00:17:50]
that it succeeded until, you know, the user clicks dismiss or something like that.
[00:17:55]
Right.
[00:17:56]
These are the kinds of patterns that they can be tricky to do with plain Elm, but sometimes
[00:18:01]
people don't realize that it's quite doable to get the same result by wrapping your Elm
[00:18:08]
app.
[00:18:09]
So sure, you can't, you can't do these things easily and you have to do a lot of like manual
[00:18:13]
tracking of these low level details of like, check when I initially did this thing and
[00:18:18]
then wait 10 seconds and it can sort of clutter this like logic of like, what am I trying
[00:18:25]
to do?
[00:18:26]
And you want this like declarative effect, maybe.
[00:18:29]
So what you're talking about is having something that wraps the updates mostly in a way that
[00:18:34]
is not the update that we usually use.
[00:18:38]
Yeah.
[00:18:39]
And there are different ways to do it.
[00:18:41]
I mean, you could, yeah, you could, you could wrap update, you could wrap init as part of
[00:18:45]
that.
[00:18:46]
You could wrap the view too.
[00:18:48]
Yeah, exactly.
[00:18:49]
If it kind of depends, you know, maybe you wrap the view and you control showing the
[00:18:54]
modal or not as part of that.
[00:18:56]
And you know, you, so whatever view the sort of child page is, is telling you to render,
[00:19:03]
you say like, okay, but if something has told me to show a modal, then I'm going to be displaying
[00:19:08]
that and I'm going to show or hide that based on my own logic as the sort of top level rendering
[00:19:15]
logic for the app.
[00:19:17]
And it manages the state for that.
[00:19:19]
And it, you know, if you click outside of the modal, it's going to hide the modal and
[00:19:24]
all these things.
[00:19:25]
So you can achieve these same things with pure Elm.
[00:19:29]
It's just that you have to get a little bit creative about how you architect your Elm
[00:19:33]
application.
[00:19:34]
Yeah, exactly.
[00:19:35]
I think there's a reason why we ended up with the Elm architecture.
[00:19:39]
So it's something that you could look into, but it's a risky one.
[00:19:43]
You can mess things up quite easily, probably.
[00:19:47]
Mess things up by having a wrapper app that acts as the sort of effect manager.
[00:19:53]
Yeah.
[00:19:54]
Yes.
[00:19:55]
You can make things very entangled.
[00:19:57]
Yes.
[00:19:58]
You can drop commands.
[00:19:59]
I don't know.
[00:20:00]
Yeah, that's right.
[00:20:01]
And you know, perhaps a topic for another episode, but it can become quite convoluted
[00:20:06]
when you have a lot of nesting of your Elm state.
[00:20:10]
And Elm tends to work out a lot nicer when you don't try too hard to sort of have this
[00:20:16]
componentized style wrapping of state where you say each little piece is going to have
[00:20:22]
its own state that it manages and then passes information and events back and forth.
[00:20:29]
The sort of parent child component communication model tends to be very painful in Elm because
[00:20:36]
there's all this extra wiring you need to do and all this overhead.
[00:20:39]
So you're right.
[00:20:40]
You have to be very careful about how you structure it.
[00:20:43]
But I think if you're very careful and deliberate about where you choose to create these sort
[00:20:48]
of higher level effects, like a modal that you say, this is like a standard thing for
[00:20:53]
our app.
[00:20:54]
You don't try to get too obsessive about hiding internal state of these different pieces,
[00:21:00]
but you say like, hey, listen, if I click to a new page, then I want this flash message
[00:21:05]
to hide.
[00:21:07]
And the place that has access to that information of, am I changing to another page?
[00:21:13]
That should be the responsibility of something above it.
[00:21:17]
That's not trying to have this component style way of architecting your apps.
[00:21:22]
That's just like, no, this really needs to be the responsibility of the thing that's
[00:21:26]
in charge of changing the view to render the view of the next page when I change sort of
[00:21:33]
thing.
[00:21:34]
Yeah, try to keep things explicit like we are used to in the Elm code that we love.
[00:21:39]
Exactly.
[00:21:40]
Yeah.
[00:21:41]
Don't just for the sake of hiding internal state, try to make everything a component,
[00:21:45]
but really think about the choice.
[00:21:47]
And it's not just these sort of higher level effects that you can use this wrap wrap pattern
[00:21:53]
for.
[00:21:54]
You can use that same pattern.
[00:21:56]
In fact, this is what Elm pages does.
[00:21:58]
Elm pages sort of wraps your Elm app.
[00:22:01]
So it feels like you're just doing something that's equivalent to browser.application,
[00:22:06]
but it gives you this broader palette of things.
[00:22:11]
So you can say, you have access to saying what you want to put in the head tags for
[00:22:15]
your SEO, and you have the ability to generate files.
[00:22:19]
You can give some Elm functions that get your metadata for all of your pages.
[00:22:26]
And based on that, you could generate an RSS feed or an arbitrary file.
[00:22:31]
That's not something that Elm can do.
[00:22:32]
But by wrapping the user's Elm app, I can pretend that Elm can do that and give this
[00:22:39]
sort of first class experience to users where as long as the types are happy, they get very
[00:22:44]
predictable behavior because the framework abstracts that away.
[00:22:48]
So that's another example of this wrapper pattern.
[00:22:51]
And you can actually achieve quite a lot if you create a framework that does this.
[00:22:56]
If you wanted to create a tool for rendering command line interfaces with pure Elm code,
[00:23:05]
then you could create a similar abstraction where you could even have a model view update
[00:23:10]
style where your view type is not HTML, but it's some sort of abstraction for displaying
[00:23:17]
terminal UIs.
[00:23:19]
And you can use ANSI color codes.
[00:23:21]
And you can have these values where you create your own version of the Elm architecture for
[00:23:27]
a CLI and wrap things, and you can give users an experience.
[00:23:31]
So it's difficult to nail that experience and get the abstraction just right.
[00:23:35]
But I think it's good to be aware that that's possible.
[00:23:38]
And it's good to be aware of that when you're choosing tools to use.
[00:23:42]
It's good to understand how these things work under the hood so they feel a little less
[00:23:46]
magical too.
[00:23:47]
Yeah, definitely.
[00:23:49]
So another way you can extend Elm is by using platform.worker to create headless apps.
[00:23:53]
Yeah, so apps that you run through something else than a browser.
[00:23:58]
So with Node.js, with some Haskell code, somehow Rust code, whatever spawns something that
[00:24:06]
runs Elm or JavaScript.
[00:24:09]
So for those who don't know, a platform worker is just a program in Elm that gets information
[00:24:15]
from ports or subscriptions and the init function and has an update function, which is the one
[00:24:21]
that you know from the Elm architecture, which returns commands and returns an updated model.
[00:24:27]
So you can use that to do asynchronous operations in Elm and run it from JavaScript.
[00:24:34]
Yeah, I mean, it's basically an Elm app without a view function.
[00:24:40]
So I use a headless worker, for example, for the Elm GraphQL code generator tool.
[00:24:46]
So there are actually technically two code bases in the Elm GraphQL Monorepo.
[00:24:52]
There's one directory of source code for the actual Elm package itself, which gets published
[00:24:58]
in the package repo.
[00:24:59]
And then there's the generator tool.
[00:25:02]
And that's something that I publish with NPM.
[00:25:05]
And it's just a headless worker app.
[00:25:09]
And it reads in some files from Node.js.
[00:25:13]
And then it passes that into Elm.
[00:25:16]
And then Elm does all of the heavy lifting.
[00:25:18]
So it basically reads in the data that it needs to, if it needs to read the schema from
[00:25:23]
a file or whatever it needs to do to get that data.
[00:25:27]
And then Elm GraphQL sends out all the data.
[00:25:30]
And it says, please generate these files, which Elm can't do, but a port can.
[00:25:34]
And then the ports just write all of those files.
[00:25:37]
I believe you do something similar in Elm review, right?
[00:25:40]
You use a platform worker for that?
[00:25:42]
Yeah, exactly.
[00:25:43]
I think that most tools that are written at the partially in Elm use that technique.
[00:25:49]
Just run it in Node.js, start an Elm application, potentially generate one, like I do in Elm
[00:25:55]
review, and give it the data that you need to do the analysis or that you need.
[00:26:02]
So in the case of Elm review, we build an application from the configuration that you
[00:26:07]
gave, which is an Elm.
[00:26:08]
So we move that around, we create a project, we compile it, and then we run it.
[00:26:14]
Node.js collects a lot of data, like file contents, and it sends it to the Elm worker,
[00:26:21]
which then gives us things back, in this case, the analysis of what is wrong in your project.
[00:26:28]
So yeah, I think that's the one that most people use.
[00:26:31]
And there are quite a few examples that you can look at that we'll probably link to or
[00:26:36]
talk about later.
[00:26:37]
Yeah, it's a very powerful pattern.
[00:26:39]
And it allows you to almost create the illusion, kind of like we talked about before, to the
[00:26:44]
user, that they're just creating an Elm app, but suddenly they have access to all this
[00:26:49]
additional data, you know, in the case of Elm review, they just get the AST of their
[00:26:54]
entire repository, of their entire source code tree, right?
[00:26:58]
And of course, that's not something that they can get directly from Elm, but you can give
[00:27:03]
them the appearance that they do because they just write some functions in pure Elm, and
[00:27:09]
they just have that.
[00:27:10]
So you just are able to bootstrap their app with all these nice things that you provide
[00:27:15]
as a framework.
[00:27:17]
And you can even, you know, it's a very nice pattern because it allows you to do pure Elm
[00:27:21]
configuration as well.
[00:27:22]
So often I, when I see a lot of configuration where, you know, there are all these YAML
[00:27:28]
configuration files or JSON configuration, it always makes me think like, I mean, if
[00:27:35]
we, if we have this nice language that gives us, you know, data types, type safety, functions,
[00:27:42]
all these units for creating data and managing data and abstracting data, why couldn't we
[00:27:49]
use that to create our configurations?
[00:27:51]
And when you use this kind of pattern, you know, basically having like a little node
[00:27:56]
JS or whatever tool you use, that's going to actually call into the user's Elm code,
[00:28:02]
you can do quite a bit with that and allow them to do configuration in pure Elm, which
[00:28:07]
opens up a lot of really cool stuff.
[00:28:09]
Well, the very cool stuff is that you restrict what configuration is possible.
[00:28:14]
Yes.
[00:28:15]
You don't open things, you close things.
[00:28:17]
And that is actually the thing that we like.
[00:28:20]
Right.
[00:28:21]
And you could even, you know, share things between something like in the case of Elm
[00:28:26]
pages, for example, Elm pages allows you to generate a manifest.json, which it's, it's
[00:28:33]
actually taking that information from your pure Elm configuration and passing it into
[00:28:39]
Webpack.
[00:28:40]
But what that allows you to do is if you want to have a title that you use for your manifest.json,
[00:28:46]
if you want to share that title somewhere in your Elm UI, you can use modules the way
[00:28:53]
you normally do in Elm code and use all the same abstractions and ways of organizing your
[00:28:58]
code.
[00:28:59]
But you can, you know, instead of having a configuration file where you try to keep it
[00:29:03]
in sync in this JSON file in one place and this place you're using it in the UI in another
[00:29:07]
place, it's all just there in Elm, which is quite handy.
[00:29:10]
Yeah.
[00:29:11]
Another thing that is quite cool with Elm review is that since the configuration is
[00:29:16]
in Elm and the configuration is mostly Elm review rules written in Elm, you can publish
[00:29:23]
them.
[00:29:24]
You can share them.
[00:29:25]
Yes.
[00:29:26]
Yes, exactly.
[00:29:28]
And it's really interesting, right?
[00:29:30]
Because you publish them, like you say, like an Elm review rule is, you know, whatever.
[00:29:37]
Code.
[00:29:38]
It's like a function that, yeah, it's just code.
[00:29:39]
It's like a function that has a particular signature, right?
[00:29:42]
Yeah.
[00:29:43]
If you publish a function like that, then if you want to make it configurable, you could
[00:29:48]
make your Elm review rule configurable with pure Elm data.
[00:29:51]
And then you have that function take another piece of data, you pass that in, and now you've
[00:29:57]
got a configurable Elm review rule and the user can pass in a bit of Elm data.
[00:30:03]
Maybe they, you know, maybe they're passing in a bit of configuration to multiple Elm
[00:30:07]
review rules or, you know, whatever.
[00:30:09]
So it's just a really powerful technique.
[00:30:13]
In the case of Elm review, another thing that I find really cool is that you can, since
[00:30:19]
it's just Elm code, you can copy paste it to your project.
[00:30:22]
So something that I've noticed in the ESLint community in JavaScript that you can only
[00:30:28]
use rules from plugins that are published on NPM.
[00:30:33]
So people keep complaining for years and years that something has not been implemented, that
[00:30:38]
a bug has not been fixed in years.
[00:30:44]
And in the Elm land, I would just say, copy paste it, fix it yourself.
[00:30:49]
And don't complain for years.
[00:30:51]
I don't know if you can sense the personal frustration about that.
[00:31:00]
Years of experience waiting for ESLint rules to be fixed.
[00:31:03]
No, being on the maintainer side and abandoning projects and people complaining years later.
[00:31:10]
Like, I'm not there anymore.
[00:31:14]
It's nice to empower people to, you know, yeah, maintain things if they need to.
[00:31:19]
Yeah.
[00:31:20]
Exactly.
[00:31:21]
That's the kind of thing we have here.
[00:31:22]
So introspection, we kind of talked about that with Elm review, right?
[00:31:26]
So you want to talk about that pattern a little bit?
[00:31:28]
I think it's in Java where you can do introspection where you can get details of how things were
[00:31:34]
implemented.
[00:31:35]
Yeah.
[00:31:36]
You can use the reflection API, which reflection.
[00:31:39]
I have seen it abused really badly.
[00:31:42]
And I'm sure, you know, people with Java experience can relate to that.
[00:31:47]
It can, you know, it's the kind of thing that you're like, why the heck is this code not
[00:31:51]
working?
[00:31:52]
And then, oh, you need to name the class something else because it's depending on that in a weird
[00:31:57]
way.
[00:31:58]
I have only heard about reflection in horror stories.
[00:32:03]
So yeah, you can do things based on how the code is written.
[00:32:08]
So what Elm review does is it gives you the AST of your Elm code, and then you manipulate
[00:32:15]
it in Elm code to do whatever you want.
[00:32:19]
In this case, report errors.
[00:32:21]
Introspection allows you to look at how things were written and then do something about it.
[00:32:26]
So you could do code generation with it.
[00:32:29]
So I think it's with your project TypeScript operation.
[00:32:32]
Oh yeah.
[00:32:34]
I have a project called Elm TypeScript Interop.
[00:32:37]
Yeah.
[00:32:38]
So you look at the Elm code and then you find, you figure out what the types are for it,
[00:32:44]
and then you generate TypeScript code.
[00:32:46]
Exactly.
[00:32:47]
I generate a TypeScript declaration file, which tells TypeScript, these are the types
[00:32:53]
of this code.
[00:32:54]
So when you import your Elm code, it knows I'm going to have app.ports.portname.send
[00:33:02]
or app.ports.portname.subscribe or whatever it is.
[00:33:07]
And it gives you auto completion and type safety.
[00:33:09]
It knows what the types of the payloads are going to be by introspecting the Elm AST
[00:33:15]
of your project and all of the types of the ports.
[00:33:17]
Yeah.
[00:33:18]
So it's code generation the other way around.
[00:33:20]
Yeah.
[00:33:21]
It's code generation with the import source of the code generation being the actual Elm
[00:33:26]
AST.
[00:33:27]
And yeah, that's right.
[00:33:28]
It's code generation for a different language, not code generation for Elm.
[00:33:33]
But it does extend the Elm guarantees.
[00:33:36]
So it's in the Elm philosophy, in my opinion.
[00:33:39]
I think so.
[00:33:40]
Yeah.
[00:33:41]
Yes.
[00:33:42]
And we'll get to techniques that make that feel maintainable and easy to use.
[00:33:47]
Let's get through the rest of these techniques.
[00:33:49]
We've only got a few more here.
[00:33:50]
So well, we've kind of talked about ports and flags.
[00:33:54]
And that's sort of a vanilla way of extending things.
[00:33:58]
And I don't think we have to say too much about that.
[00:34:01]
No, we use it all over the place with all of the techniques that we just talked about.
[00:34:06]
Right, and it can have limitations with how you can sort of wrap things up to have this
[00:34:12]
seamless feeling that you can't use it incorrectly.
[00:34:16]
It feels a lot more like you're afraid that you'll wire something up incorrectly.
[00:34:20]
So if you can have the support of some sort of automation to make sure that you're not
[00:34:25]
messing something up, that can be nice.
[00:34:27]
So you can use ports and flags in tandem with those types of techniques.
[00:34:31]
And then something that's often talked about as a good approach to solving problems in
[00:34:36]
Elm, web components.
[00:34:39]
We'll get into some details of that.
[00:34:40]
But that's a great tool to have at your disposal.
[00:34:44]
You can kind of send and receive information by passing in data through attributes and
[00:34:50]
by listening for events on the web component.
[00:34:53]
Yeah, you can create custom events to make.
[00:34:56]
Yeah.
[00:34:57]
Yes.
[00:34:58]
Now, a tool that's sort of similar to code generation is transformation.
[00:35:05]
I guess you could sort of say that these are in the same category.
[00:35:09]
So there are actually a couple of tools that are pretty commonly used.
[00:35:15]
For example, create react app uses something.
[00:35:19]
It's built into the web pack configuration it uses under the hood, and it's Elm Asset
[00:35:23]
web pack loader.
[00:35:25]
So that just does like a little string interpolation to find stuff in your Elm source code that's
[00:35:32]
using a particular string pattern.
[00:35:33]
And then it changes that to point to a particular file asset.
[00:35:38]
And the actual web pack pipeline can give you some feedback if you refer to an image
[00:35:43]
that doesn't exist, for example.
[00:35:45]
Right.
[00:35:46]
So you use it to link to images or to link to CSS classes or stuff like that.
[00:35:54]
That's all that I've seen it used for actually.
[00:35:58]
Yeah.
[00:35:59]
And it's good to know that that pattern exists.
[00:36:02]
I mean, these things all have trade offs and gotchas.
[00:36:06]
So the last one we've got here is JavaScript hacks and sort of more FFI rather than this
[00:36:13]
sort of port interop style, the recommended sort of official approach.
[00:36:20]
So this is something that you want in every project of yours, you need to use it, abuse
[00:36:26]
it.
[00:36:27]
It's just going to make your life way easier.
[00:36:31]
What could possibly go wrong?
[00:36:32]
Oh, no, no.
[00:36:35]
So there are a lot of hacks and we will not go into them because we don't like them for
[00:36:41]
multiple reasons.
[00:36:43]
Often it's something that works because Elm was written in a way.
[00:36:47]
So it's implementation dependent.
[00:36:50]
So that means that it will potentially break in a future version of Elm.
[00:36:55]
And when things break without you knowing, that's not a good sign generally.
[00:37:00]
Exactly.
[00:37:01]
Exactly.
[00:37:02]
Yeah.
[00:37:03]
If I'm browsing around for tools to accomplish a particular task and I find, I don't know,
[00:37:10]
I find Elm review and then I find that there's another tool that does the same type of thing,
[00:37:17]
but it sort of patches the AST or, you know, I don't know if Elm review would be a great
[00:37:23]
use case for this kind of pattern even.
[00:37:26]
But if I'm looking at a tool and I see that it's using this approach, I'm going to be
[00:37:31]
very hesitant to use it because I'm not going to have a high level of confidence that it's
[00:37:35]
going to be, well, you know, if there's a new version of Elm, I don't have a high level
[00:37:40]
of confidence that it's going to survive to the next version because it's potentially
[00:37:45]
depending on internals that will change.
[00:37:48]
I don't have a high degree of confidence that it will handle all the edge cases of, you
[00:37:53]
know, certain bugs that may arise because it's hacking, right?
[00:37:56]
So in general with hacks, I just don't trust them.
[00:37:59]
And if you can avoid it, then I prefer not to depend on them.
[00:38:03]
I prefer not to use them to solve problems.
[00:38:05]
And so the one example of something that uses this approach that is something that I depend
[00:38:12]
on is ElmHot, which is the underlying tool that's used for the ElmHot web pack loader.
[00:38:19]
And what it does is it, you know, it hooks into all these internals about how Elm manages
[00:38:26]
state and it hot swaps in your Elm code when you, when you get a new version of your compiled
[00:38:32]
Elm code and retains your state, which of course is, it's a hack and it could be a hack
[00:38:38]
that, you know, if it's built into the language, then it's not a hack.
[00:38:42]
It's like a language feature, right?
[00:38:44]
Because it is officially supported.
[00:38:46]
Exactly.
[00:38:47]
Right.
[00:38:48]
So there's this like official commitment to sort of continue having the internals support
[00:38:53]
that use case.
[00:38:54]
And so, you know, if it breaks, if you're depending on internals and they break, okay,
[00:38:59]
what can you do?
[00:39:00]
If you're depending on internals and it, and it breaks in a way where you can't even accomplish
[00:39:05]
that same type of thing, then that tool is just going to die.
[00:39:08]
And we've, we've seen that kind of thing happen in the past.
[00:39:10]
So it's ideal if you can avoid depending on internals.
[00:39:15]
It's really just, I have a very low level of trust if I see that a tool is depending
[00:39:20]
on this.
[00:39:22]
And if it's some code that I'm using professionally at my workplace, I wouldn't feel very good
[00:39:29]
about making the code base depend on something like that.
[00:39:33]
Elm hot is one sort of example that I feel comfortable with because I mean, first of
[00:39:38]
all, it's like a developer experience improvement that if it goes away, it's not the end of
[00:39:43]
the world.
[00:39:44]
It's, you know, it's convenient, but it's such a standard community tool that it almost
[00:39:51]
feels like an official tool.
[00:39:53]
Yeah.
[00:39:54]
And it's not going to break your production.
[00:39:55]
It's not going to lose any guarantees.
[00:39:57]
Well, maybe in developments when you change things in things that in ways that it wasn't
[00:40:02]
expecting.
[00:40:03]
Yes.
[00:40:04]
But what constitutes a hack is something that uses something that is not documented.
[00:40:10]
So if the Elm compiler or the Elm project documents something as okay to use for certain
[00:40:16]
purposes and use it for those purposes, it's fine.
[00:40:20]
Go use it.
[00:40:21]
That's why we use ports.
[00:40:22]
That was why we use flags and web components, but don't go to changing what is in your Elm
[00:40:28]
stuff.
[00:40:29]
Don't go changing the output source code because it's not going to be maintainable in the long
[00:40:35]
run.
[00:40:36]
Right.
[00:40:37]
It's most certainly going to break.
[00:40:38]
And it's not going to be fun to debug that or just to debug anything that you made using
[00:40:46]
those techniques, even if there's no major version of Elm changing or patch version of
[00:40:51]
Elm.
[00:40:52]
Yes, exactly.
[00:40:53]
Exactly.
[00:40:54]
Okay.
[00:40:55]
I think it's pretty clear where we stand on hacking the actual Elm source code in order
[00:41:01]
to accomplish a task in order to solve a problem.
[00:41:04]
So there are plenty of gotchas there.
[00:41:08]
First we've kind of covered our sort of set of possible techniques for extending Elm.
[00:41:16]
Let's just kind of quickly review all of those topics and then talk about some of the gotchas
[00:41:21]
and considerations for these different techniques.
[00:41:23]
So we talked about code gen, macros, Elm wrapper apps, headless apps, platform worker, introspection,
[00:41:33]
ports and flags and web components, the sort of official techniques, transforming source
[00:41:37]
code like web pack transformers, and then JavaScript hacks.
[00:41:42]
So maybe in these official channels, what are the gotchas?
[00:41:47]
What are the good times to use them?
[00:41:50]
When would you use a port?
[00:41:51]
When would you use a web component?
[00:41:53]
I haven't played too much with web components.
[00:41:55]
So I usually use ports for most things.
[00:42:01]
I'm a fan of web components.
[00:42:03]
It's just that I haven't had the chance to play with this or it's not my go to solution.
[00:42:12]
There's also the fact that you need to add a polyfill for certain browsers.
[00:42:17]
Yes.
[00:42:19]
So that is one gotcha, so you need to send extra bytes over the wire to make it work
[00:42:24]
on all browsers.
[00:42:27]
IE 11, for instance.
[00:42:31]
And Safari has a couple of weird behaviors with web components.
[00:42:37]
Safari is the new IE.
[00:42:41]
In general, you're going to have a pretty seamless experience using web components,
[00:42:46]
but certainly there are a few gotchas to make sure that you're handling any polyfills and
[00:42:51]
things that you need to.
[00:42:53]
You need to use attributes.
[00:42:56]
And they're a bit tricky sometimes.
[00:42:59]
But otherwise, it's a very fun feature.
[00:43:01]
I definitely find that it can be difficult to get the wiring right.
[00:43:06]
Really I think that there could be a really great opportunity to create a tool to make
[00:43:11]
it easier to build web components for use with Elm and to enable you to kind of capture
[00:43:19]
the contract of what data you can pass into the web component and what events can come
[00:43:23]
out and what the types will be.
[00:43:25]
I think there could be a really cool like code generation tool to help with that.
[00:43:29]
But just with with vanilla web components versus ports, my rule of thumb that I think
[00:43:34]
about is ports are imperative and web components are declarative.
[00:43:40]
And there's nothing wrong with that.
[00:43:41]
Sometimes you need something that's more imperative where an event occurs and you say, okay, I
[00:43:46]
need to call some NPM library to perform some side effect, whatever.
[00:43:54]
Maybe it's an NPM wrapper that performs some authentication or something like that.
[00:44:01]
For whatever reason, you need to use JavaScript to do it.
[00:44:05]
Or if you want to show an alert, everybody knows how great of a user experience it is
[00:44:09]
to show alerts in your browser.
[00:44:11]
So things like that, you need to use a port.
[00:44:14]
I mean, I guess you could technically use web component, but it feels a little weird
[00:44:18]
because it's like an event.
[00:44:20]
Yeah, you could definitely.
[00:44:21]
Yeah.
[00:44:22]
So then web components are very declarative.
[00:44:26]
So if you're sort of saying, if I tell you to present this data type, I want you to do
[00:44:33]
some JavaScript to tell you how to present it.
[00:44:37]
But you can think of it in this higher level way of presenting this, that sort of declarative
[00:44:41]
way of saying present this high level thing.
[00:44:44]
Web components tend to work quite nicely.
[00:44:46]
So like I've really enjoyed using, you know, I had this page of events that I'm showing
[00:44:51]
and I want it to show in the user's local time.
[00:44:55]
And the Elm time API doesn't quite give what I need because it's a long story, but there
[00:45:02]
are like time zones and time offsets and I needed the time zone and Elm time doesn't
[00:45:07]
really support that.
[00:45:08]
And so the browser has this sort of nice platform that it provides for internationalizing time.
[00:45:15]
And so it's going to do things like, you know, maybe in some countries they'll show the month
[00:45:21]
first and in some countries they'll show the day first.
[00:45:24]
So you don't sort of tell it, show the day, then show the month, then show the year.
[00:45:29]
You say, I want to see the day, the month and the year.
[00:45:32]
You can figure out how to present that in the user's local time.
[00:45:35]
And I want the short version of this or the long version or the numeric.
[00:45:38]
I want leading zeros.
[00:45:39]
And it just figures out how to put everything together.
[00:45:42]
It's quite nice.
[00:45:43]
This is a great example of a use case for a web component because you can even create
[00:45:49]
a little, you know, Elm function that creates that web component.
[00:45:53]
It just does, you know, HTML dot node for your web component and passes in the right
[00:45:58]
information, but your Elm function can abstract that.
[00:46:01]
So you pass in, you know, an Elm time, you know, POSIX time or, you know, whatever your
[00:46:07]
data type is.
[00:46:09]
So I've found that that's quite a nice experience.
[00:46:11]
It would be even nicer with some sort of framework that helps you make sure you're keeping these
[00:46:17]
events and data types that you pass into the web component in sync.
[00:46:21]
Okay.
[00:46:22]
So in terms of the gotchas, another thing I wanted to cover is how do you, you know,
[00:46:27]
we kind of talked about this philosophy that what you're trying to do is make it feel like
[00:46:32]
a first class experience to use these extended features that Elm doesn't have, but you want
[00:46:39]
it to feel like Elm has these features, right?
[00:46:42]
Yeah.
[00:46:43]
So you want it to feel like Elm gives you access to the ASD when you're writing an Elm
[00:46:47]
review rule, even though it doesn't, but you can make it feel like it does.
[00:46:51]
You want it to feel like Elm has, you know, the ability to consume a GraphQL API with
[00:46:58]
a nice, you know, API that's in sync with your GraphQL schema, even though Elm doesn't
[00:47:03]
do that.
[00:47:04]
But if you have the right developer experience, you can achieve that.
[00:47:08]
So maybe let's talk a little bit about what's the alternative to having that sort of experience
[00:47:14]
and what are the gotchas.
[00:47:15]
One thing I've experienced is using these sorts of abstractions and tools that, you
[00:47:20]
know, maybe they do code generation or whatever the technique they're using for extending
[00:47:25]
Elm, but maybe there's not a clear source of truth.
[00:47:29]
That's sort of one pain point I've seen with this type of technique.
[00:47:32]
So you know, okay, you have to change the code here, but you also have to make sure
[00:47:36]
that this code over here matches that.
[00:47:38]
And so there's like a fuzzy abstraction where there's not a clear source of truth, but the
[00:47:43]
source of truth is distributed several places and you have to second guess yourself, you
[00:47:49]
know, you're not quite confident that you've wired everything incorrectly.
[00:47:52]
Yeah.
[00:47:53]
So basically every time you see in the documentation something that says, note, by the way, don't
[00:47:58]
forget to...
[00:47:59]
Yes.
[00:48:00]
Yeah.
[00:48:01]
Exactly.
[00:48:02]
That's a smell that maybe you don't have a clear source of truth.
[00:48:06]
That's going to be a bad user experience.
[00:48:08]
And that means, you know, as you're implying, right, they're writing a caveat that says
[00:48:13]
something might go wrong.
[00:48:15]
Well, it's not an airtight abstraction.
[00:48:17]
You've allowed for something to go wrong, but the whole point is you want to give these
[00:48:21]
guardrails that say, Hey, you know, if you give me this thing, if it compiles, then this
[00:48:26]
will happen and you don't have to worry about anything else.
[00:48:29]
Yeah.
[00:48:30]
Another pitfall I've seen with this type of technique is not having a clear separation
[00:48:35]
between machine generated code and user maintained code.
[00:48:41]
And so I think actually a common technique that I've actually found, found to be really
[00:48:46]
useful is to get ignore your generated code because you want a clear separation and you
[00:48:52]
want it to be, you know, just like your generated Elm code, right?
[00:48:55]
You don't care about that code.
[00:48:57]
Like ideally you want to like say import main.elm, import source slash main.elm from your JavaScript
[00:49:03]
entry point, right?
[00:49:05]
Yeah.
[00:49:06]
You don't want to say like import generated.elm.js or something like that.
[00:49:11]
You want it to be this more declarative thing where you're not depending on the low level
[00:49:15]
internals.
[00:49:16]
And we're going to have to do the wiring manually and where you can mess up the wiring.
[00:49:23]
Right.
[00:49:24]
It's nice if you can have a clear separation where the internals are not exposed.
[00:49:29]
And if you, if you separate the generated code from the user maintained code, it creates
[00:49:36]
this more clear separation where you're, you're not at risk of digging into the internals
[00:49:41]
or messing something up that the machine representation depends on in a confusing way.
[00:49:46]
So it's, it's very difficult to get the user experience right where a user has an interaction
[00:49:51]
with code generation that feels predictable, easy to find what you're looking for, well
[00:49:58]
documented.
[00:49:59]
Also, if you'd like generating code, make sure that your public APIs look nice.
[00:50:03]
Make sure that, you know, you can even have doc comments.
[00:50:06]
And in a lot of editor tooling, if you hover over your functions that you're calling in
[00:50:10]
the generated code, you can see the documentation comments.
[00:50:13]
So you can generate those.
[00:50:16]
We talked about macros earlier too, and there are a few gotchas about those too.
[00:50:21]
One of the scary things about macros is when you start using it for code that does not
[00:50:26]
look like Elm code.
[00:50:27]
So when you try to add syntactic sugar that with symbols that do not exist, the problem
[00:50:32]
with that is that even though we will make your code maybe very simple, that a lot of
[00:50:38]
tooling will not work out of the box.
[00:50:41]
So with code generation, you just generate codes and it's Elm code, correct syntax, good
[00:50:50]
Elm code.
[00:50:51]
But with macros, no tooling will work.
[00:50:54]
Elm review will not work.
[00:50:55]
Your editor will not work.
[00:50:57]
The Elm compiler will not work.
[00:50:59]
Elm test will not work.
[00:51:01]
Pretty much nothing will work unless you ask the tooling authors to support it.
[00:51:07]
And if you have been in the Babel land, that's what's going on everywhere.
[00:51:14]
There's a new JavaScript syntax proposal.
[00:51:17]
People ask every tool out there, every maintainer of every tool out there to add support for
[00:51:24]
it.
[00:51:25]
A new TypeScript feature comes in.
[00:51:26]
People want that in their tooling, in their editors.
[00:51:30]
And from the maintainers perspective, it's a lot of work.
[00:51:35]
And from the user's perspective.
[00:51:36]
Yeah, too.
[00:51:37]
Yeah, definitely.
[00:51:38]
Like I was just writing a Jest test for actually one of these sort of types of tools that we're
[00:51:45]
talking about where it is generating like a framework that allows you to use pure Elm,
[00:51:50]
but under the hood there's JavaScript and stuff happening, right?
[00:51:53]
So I was using some Jest for some generated code and I wanted to do like a snapshot test
[00:51:58]
that I'm generating the code correctly and pulling in stuff that I need from some JavaScript
[00:52:03]
APIs.
[00:52:04]
And well, I needed to, like I was looking through the documentation and it was explaining,
[00:52:10]
oh, and if you need to use certain Babel features, here's how you set up Jest in order to pull
[00:52:16]
in these Babel features and here's how you configure it to do that.
[00:52:19]
Like it's like a first class thing that's supporting your tests calling code that's
[00:52:24]
running through Babel.
[00:52:26]
And then here's how you run it through Webpack.
[00:52:28]
Yeah.
[00:52:29]
Well, what if you happen to use Rollup?
[00:52:32]
What if you're using something that uses Rollup instead of Webpack or you use Parcel?
[00:52:37]
Okay.
[00:52:38]
So now are you going to go ask the Jest maintainers team to add support for Parcel too?
[00:52:46]
And I want to use my Parcel config, are you going to hack that?
[00:52:48]
Are you going to move over to Webpack?
[00:52:50]
Oh, by the way, there's a new tool to do that now.
[00:52:53]
Yes.
[00:52:54]
Let's add it to support for it's everywhere.
[00:52:56]
It's like the open close principle where you want your code to be open for extension and
[00:53:01]
closed for modification.
[00:53:04]
And the type of technique we're talking about where you rely on these macros that transform
[00:53:09]
your your code with some syntactic sugar, you end up really you're modifying the syntax.
[00:53:17]
So rather than having a way of extending what you can do with the language, you're modifying
[00:53:23]
the language itself and you're going to have a bad time.
[00:53:27]
I mean, there's a reason for the open close principle.
[00:53:30]
I think it's in general a good thing to keep in mind for maintainability and it applies
[00:53:35]
just as much to tools as it does for your own internal APIs.
[00:53:39]
In fact, it especially applies to frameworks.
[00:53:42]
So this reminds me of another point in this general area is that if you build your tools
[00:53:48]
with an abstraction that's opinionated and provides a very narrow set of things that
[00:53:54]
it does that can be good, but you also want to balance that with are you going to be making
[00:54:01]
it impossible to do for certain things?
[00:54:04]
And is that going to require your users to hack around it?
[00:54:06]
Or are they going to get into a dead end?
[00:54:08]
So what I see a lot is I see these tools that in my opinion, create like very low level
[00:54:13]
extension points that to me are opening it up for modification, not for extension, because
[00:54:18]
they haven't provided this platform with extension in mind.
[00:54:23]
And I think it's very nice when you can have tools that give you a nice high level abstraction,
[00:54:29]
but it also gives you the right extension points so that you can you can accomplish
[00:54:34]
what you need to without going into low level things, dangerous things, things that sort
[00:54:39]
of break the contract.
[00:54:40]
Those are all sort of red flags to me that you've opened up internals that belong as
[00:54:47]
internals.
[00:54:48]
Okay, well, I think we had a pretty good overview of a lot of techniques, we probably missed
[00:54:52]
a lot of them.
[00:54:54]
And you can use them together to do pretty cool things and pretty awful things.
[00:55:00]
Yes.
[00:55:01]
So yeah, I think the core thing to remember is to keep the Elm philosophy, try to make
[00:55:06]
things look like Elm, try to make things do like Elm.
[00:55:10]
Anyway, yeah, don't don't rely on implementation details.
[00:55:15]
Don't make things that the language is actively working against.
[00:55:20]
Yeah, I think maybe a piece of advice that I would give to anybody who's looking to create
[00:55:26]
a tool like this, or also that I'd give to my future self is really think about the mental
[00:55:32]
model for users.
[00:55:34]
You know, what mental model are you giving to users?
[00:55:36]
If you were to describe how this works, you know, you'd say, think of it like this.
[00:55:44]
And what you need to be sure of is that when you say think of it like this, are there any
[00:55:49]
leaks?
[00:55:50]
Is it like, think of it like this, except there's this one caveat.
[00:55:53]
And if you do this one thing, then it'll actually behave like this.
[00:55:56]
And then you don't have a clear mental model.
[00:55:59]
And you have a sort of, you know, leaky abstraction.
[00:56:02]
And not it's not a, you don't have maybe a cohesive source of truth that your that your
[00:56:08]
tool is using and a cohesive set of behaviors that are going to be predictable and intuitive
[00:56:13]
for the user.
[00:56:15]
So keep that in mind, and you know, strive for the right type of simplicity where you're
[00:56:23]
opinionated about the essential things, but you leave it open to extension for the appropriate
[00:56:31]
things so users won't get into a dead end.
[00:56:34]
I think Elm SBA has done a really nice job creating like a simple abstraction that is
[00:56:41]
lightweight and not so opinionated that it, you know, paints you into a corner.
[00:56:47]
And I know that Ryan has done some iteration on that.
[00:56:50]
Initially he started with having it manage page transitions and things like that.
[00:56:54]
And then he realized like, this is too heavyweight and too complicated and becomes difficult
[00:56:59]
to extend.
[00:57:01]
And that's the thought process.
[00:57:02]
You're not going to get it right on the first time, but pay attention to is this a simple
[00:57:07]
mental model?
[00:57:08]
Does it because that's the thing that happens when you get it right.
[00:57:12]
You have at the same time a simple mental model and something that's extensible.
[00:57:19]
I love that you now see Elm SBA.
[00:57:21]
Yeah, I decided that it was taking up too much time out of my day to have people who
[00:57:27]
were like, Oh, like a spa that sounds relaxing or like, or in your case, that doesn't sound
[00:57:36]
relaxing.
[00:57:40]
Just tell me if I'm annoying.
[00:57:44]
It's happened from enough people mentioning it that I'm like, you know, this is going
[00:57:48]
to save me time.
[00:57:49]
I'm just going to call this, I'm going to admit defeat.
[00:57:53]
I'm not going to, I'm not going to die on this Hill.
[00:57:58]
Except that I now tell you when you say Elm SBA.
[00:58:03]
There's no winning.
[00:58:04]
There's no winning.
[00:58:05]
Okay.
[00:58:06]
Well, John Doe, I hope we answered your question.
[00:58:10]
We would really like to know if this was interesting to you and especially if your name is really
[00:58:16]
John Doe.
[00:58:17]
Yes.
[00:58:18]
I'm really curious now.
[00:58:19]
Please.
[00:58:20]
We want to solve that mystery and it's totally okay to submit anonymous questions to you.
[00:58:26]
And we welcome any and all questions and we really appreciate getting everybody's thoughts
[00:58:31]
and ideas for discussions.
[00:58:33]
So well, pleasure is always your ruin.
[00:58:36]
The pleasure is all mine.
[00:58:38]
I'll see you next time.
[00:58:39]
See you next time.