spotifyovercastrssapple-podcasts

elm-pages scripts

We discuss elm-pages BackendTasks and how to run them as scripts with a single command.
January 30, 2023
#75

Transcript

[00:00:00]
Hello Jeroen. Hello Dillon. So I think our listeners may be shocked to hear but our episodes
[00:00:07]
are usually unscripted. But today we're going to do something a little bit different. I'm
[00:00:13]
never ready for you making a pun. I should be at this point. But I'm not. You knew it
[00:00:21]
was coming. No I wasn't. What are we talking about today? Today we're going to talk about
[00:00:28]
Elm Pages scripts. Scripts! That was the pun. Yeah. So we haven't talked about Elm Pages
[00:00:35]
in a while. Yes. We did talk about it already in the first episode if I remember correctly.
[00:00:42]
Yep that's right. So it's a project you've been working a long time on. And we haven't
[00:00:49]
heard about it much this year except on Elm Radio because you keep mentioning it for good
[00:00:57]
reason. You're still working on it. And yeah you've been working you've been making a lot
[00:01:03]
of things in Elm Pages. A lot of very cool things that we're going to talk about in future
[00:01:09]
episodes. But today we're just going to focus on one part of it. And that is going to be
[00:01:13]
scripts. Yes. Because from the amount of content that I can see in Elm Pages we're not going
[00:01:20]
to be able to fit that into even hour long episodes. Exactly. So yeah Elm Pages scripts.
[00:01:27]
Like what is a script? What is Elm Pages? What is this cool new thing that people want
[00:01:35]
to hear about? Yeah. So I'm really excited to see what people do with Elm Pages scripts.
[00:01:41]
So yeah and just to reiterate we are talking about the Elm Pages V3 release. At the time
[00:01:46]
of this recording it is pre-release ramping up to get the release getting some final API
[00:01:53]
changes and feedback from the community and writing some docs updating some docs. But
[00:01:58]
yes so that's what we're talking about here. Elm Pages V3. The scope of what you can do
[00:02:04]
with Elm Pages V3 is kind of huge. We will talk about it in the future but Elm Pages
[00:02:11]
V2 people may think of as a static site generator which is what it was. So Elm Pages V3 allows
[00:02:19]
you to do everything you could with V2. You can still build a static site using it. Elmradio.com
[00:02:25]
is built using Elm Pages V3 actually. It's on a pre-release and it does that by generating
[00:02:31]
static assets but you can also do server rendered pages. So the scope of Elm Pages V3 has changed
[00:02:39]
with what you can do quite a bit compared to V2. But the heart of Elm Pages is still
[00:02:45]
the same throughout all of its permutations. I've always thought of the heart of Elm Pages
[00:02:53]
as being this sort of engine that's able to like execute things on a back end and give
[00:03:00]
you back data. In Elm Pages V2 that was called data sources. So you know you could do a data
[00:03:07]
source to make an HTTP request. So like for Elmradio.com it uses a lot of local files
[00:03:16]
with markdown and pulls them in, runs a markdown parser. It's reading files. You know obviously
[00:03:25]
with Elm there's not a way to go to your local file system and read files directly. But Elm
[00:03:31]
Pages gives you ways to do that. So in V2 that tool was called a data source and you
[00:03:37]
know you can pull in data to your initial page render using a data source. So before
[00:03:43]
your static page renders you can read from a file. That was called data sources in V2.
[00:03:49]
In V3 because the scope of what Elm Pages does has changed the term data source has
[00:03:58]
been renamed and the concept has changed a tiny bit. And the reason for that is because
[00:04:04]
in V2 it was the model was much more you try to make an HTTP request. You try to read from
[00:04:11]
a file and if anything goes wrong you just stop the build and fail. And then the developer
[00:04:17]
can read the issue. They can read a nicely formatted error message and say oh this API
[00:04:25]
turned to 404. Let me fix that and then rerun the build and it succeeds. With V3 so for
[00:04:32]
example if your server rendering pages maybe you get a 404 in an HTTP request. Maybe you're
[00:04:39]
doing a post and you need to update something and you need to handle that error in a graceful
[00:04:46]
way. So that's one of the reasons why this concept has changed and become a little more
[00:04:52]
powerful and part of the reason why the name has changed. So in V3 the term is no longer
[00:04:58]
data source is now called a back end task. And in addition to that in V so back end task
[00:05:05]
it actually looks and feels a lot like the Elm core tasks.
[00:05:09]
Okay well that's not going to be very surprising then to use.
[00:05:14]
Yeah I hope so. In fact a lot of the API looks quite similar. You can do back end task dot
[00:05:21]
and then you can do back end task dot map or back end task dot map error. Now if you're
[00:05:28]
familiar with data sources in Elm pages V2 the map error part might be surprising because
[00:05:34]
in V2 there was no error. So an Elm task is task with an error type variable and a data
[00:05:43]
type variable. So if you do an Elm HTTP task it's going to give you a task HTTP dot error
[00:05:54]
and then your decoded data as the data type. And then you do task dot attempt. You have
[00:06:01]
to do dot attempt if there is an error that could happen. And then you get a message where
[00:06:09]
you can deal with that result. So you can do you know turn it into remote data or do
[00:06:15]
whatever you want to with that result. So Elm pages V3 has these back end tasks. They
[00:06:21]
can have an error. So the reason for that is because in V2 it was much more if anything
[00:06:27]
goes wrong stop the stop the line stop the assembly line stop everything. And so there
[00:06:34]
is a trade off there because that is very convenient in a lot of ways. It's more convenient
[00:06:40]
but less powerful. Yeah because for instance you can't handle the fact that an error has
[00:06:45]
happened. You can't retry an HTTP request when you know that your server is a bit flaky
[00:06:50]
or not always available. Stuff like that. So you can handle errors. Exactly. Now you
[00:06:56]
it's completely at your disposal what you do with error handling. So we'll get into
[00:07:01]
that. And one of the things that I love about back end tasks as compared to the design in
[00:07:09]
V2 with data sources is with a back end task. If there is an error in that type variable
[00:07:16]
then it has a possibility of failing. If there is no error there. So if you have you know
[00:07:21]
back end task never my data then you know it will never fail. And that's that's something
[00:07:28]
that you couldn't just look at the types in a data source in V2 and know whether or not
[00:07:33]
it's going to fail because that possible failure gets sort of tucked under the hood. It's not
[00:07:39]
represented by the types. Right. So it's much more like a task in that sense where you you
[00:07:44]
can have a task where you know that it doesn't have the possibility of failure. So if you
[00:07:49]
if you have like an HTTP task which is task HTTP dot error my data and then you can do
[00:07:58]
task dot on error and that allows you to do you know you could just say task dot succeed
[00:08:05]
in that on error and turn it into some other fallback data for example. Yeah. So you can
[00:08:12]
recover from the from the failure. Exactly. And in some cases that might make sense. In
[00:08:17]
some cases you might want to do a follow up task and try something else whatever it might
[00:08:21]
be but it's completely at your disposal. But if you if you were to do task dot succeed
[00:08:27]
in the on error then you would get you would be able to recover from that failure and then
[00:08:34]
the types would would reflect that it's not possible for it to fail anymore which is kind
[00:08:39]
of cool. So you can do the same thing with Elm pages V3 back end tasks. It's it's the
[00:08:46]
same mental model. And if the error type variable is unbound if if you don't have any errors
[00:08:54]
there then you know it's not possible for it to have an error. So that's back in tasks
[00:08:57]
back in tasks are the heart of Elm pages even more so in the in the V3 release that is coming
[00:09:05]
up soon. And and they are also the heart of Elm pages scripts. So let's talk about what
[00:09:13]
Elm pages scripts are. I almost forgot about that part. Yeah. If only we had a script that
[00:09:20]
we should follow you know exactly we got we got a little bit off script there. So Elm
[00:09:28]
pages scripts are really just a way of defining a back end task and executing it from the
[00:09:36]
command line. So if you wanted to make an HTTP request and log something to the console.
[00:09:45]
So what you would do is so the minimal setup for Elm pages scripts it actually doesn't
[00:09:52]
require any of the Elm pages application folder or anything. You don't need any route modules
[00:09:58]
defined. You don't need any of the config for Elm pages. All you need is a folder called
[00:10:06]
script. So much like for an Elm review project you have a folder called review and it's a
[00:10:13]
regular Elm project. It has an Elm.JSON Elm pages script is the same thing. So you have
[00:10:18]
a script folder that script folder has to have an Elm.JSON. So it's a little Elm project.
[00:10:23]
It has to have Elm pages as a dependency in that Elm.JSON. And and then what you do is
[00:10:29]
you do Elm pages run hello. And now if you have something in your source directories
[00:10:37]
like source slash hello dot Elm it's going to go and execute that module. So right. Yeah.
[00:10:44]
What does it mean to execute an Elm module becomes the question. Yeah. So you mentioned
[00:10:50]
it's basically running a back end task. Exactly. So back end task is used to fetch data from
[00:10:58]
HTTP or from files so you can grab the data. But then that's it. Right. You can only grab
[00:11:06]
data. You can only fetch things. What else can you do with it. Yeah. Exactly. And that
[00:11:12]
would be much more the mental model in Elm pages V2. But now there's a little bit more
[00:11:18]
of a notion of being able to do effectful things using back end tasks in V3 because
[00:11:26]
if you're doing a server rendered route and responding to a form submission which you
[00:11:31]
can do in V3 which we'll talk about in a future episode you might want to do something that
[00:11:35]
has an effect on the world. It's not just about shoveling in data to generate static
[00:11:39]
pages. So similarly a back end task is not only about grabbing data. It does let you
[00:11:47]
pull in data and map that data back and test that map back and test that. And then. But
[00:11:54]
you can also perform effects. So for example there is a back end task in the Elm pages
[00:12:01]
API called script log script log takes a string and it gives you a back end task with no data.
[00:12:11]
All right. Yeah. So that looks like back end task lowercase error because it's not possible
[00:12:17]
for it to error and unit because it doesn't have any data. So the hello world for Elm
[00:12:24]
pages script is you have your script folder you have an Elm.JSON you have source slash
[00:12:32]
hello dot Elm and in your hello dot Elm you expose a function called run. Okay. That is
[00:12:39]
the main function in a way. Exactly. Run is like the main function for an Elm pages script.
[00:12:45]
You run has the type script and and then you do script dot without CLI options scripts
[00:12:56]
dot log hello. That's hello world. So that's it. Then you run Elm pages run hello and it
[00:13:02]
prints hello world. I hope that was clear to listeners. It's always hard to explain
[00:13:09]
code all of this was not that much code. Yeah. And that's sort of the thing I hope people
[00:13:14]
take away from this is like to write a hello world script and run it is a very small number
[00:13:22]
of files that you need to create code you need to write and commands you need to run.
[00:13:28]
Notice for example that we didn't say Elm pages compile script this and then make an
[00:13:36]
index dot JS file and node and run a node script and all of that. And if we had a compiler
[00:13:42]
error or anything like that in hello dot Elm then running Elm pages run hello would tell
[00:13:48]
us about that. So it's it's a very minimal amount of code we will link to an example
[00:13:54]
of a hello dot Elm will link to a starter repo that gives you like a minimal boiler
[00:13:59]
plate so you can clone it and play around with it. But it's designed to be a very small
[00:14:05]
amount of code. And also the the abstraction of a back end task is designed to be minimal
[00:14:12]
in a way too because there's no in it. There's no update. There's no subscriptions like you
[00:14:18]
just log. You don't return a model and then a command with a logging thing. And then you
[00:14:25]
like if you wanted to make an HTTP request you just do you know back end task dot HTTP
[00:14:31]
dot get JSON give it a URL give it a JSON decoder and then back end task dot and then
[00:14:39]
and then you can log some data that you decoded. So right. It's designed to be more like the
[00:14:46]
abstraction of a back end task lets you do things in a more lightweight way especially
[00:14:51]
for this sort of mental model where it's just like just execute this thing. So it back end
[00:14:56]
task maps very nicely to the idea of a script where it's just execute this thing or fail.
[00:15:03]
Do this do this do that. And we're done. Right. Exactly. It's not updating the model. It's
[00:15:09]
not responding to user input. So it's just a sequence of tasks that it's performing.
[00:15:16]
Whereas if you had to listen to user input if you had to listen to the user typing into
[00:15:23]
an input field or an on click that would sort of break that mental model of just saying
[00:15:27]
there's one back end task it just runs and it finishes or it fails. Right. So is there
[00:15:33]
no way to listen to user prompts or could that be added. That is a that is a very good
[00:15:39]
question. I have thought about that. Wolfgang Schuster has been doing some experiments with
[00:15:45]
some with a sort of Elm Inc project where you can do things like create interactive
[00:15:53]
terminal applications in Elm. And I definitely think it could be interesting potentially
[00:15:58]
to have a way to have an init update things like that if you wanted to do something like
[00:16:03]
that. But it would be possible but it would add complexity. Yeah. But I mean even without
[00:16:10]
having to have an init update and all those things you could probably have a define a
[00:16:15]
back end tasks which succeeds with the user prompts without having this the Elm architecture
[00:16:23]
lifecycle. Exactly. That's the that's the thing is you can and you actually can. I actually
[00:16:29]
haven't tried that specific use case but you can run arbitrary Node.js code using back
[00:16:36]
and task dot custom. We'll link to the back end task dot custom module docs which explains
[00:16:42]
how to set that up. But essentially you just give it a JSON encoder and a JSON decoder
[00:16:50]
and you can define a custom back end task and that custom back end task could be a wait
[00:16:55]
user input and you could give it a prompt and you know you could block until you receive
[00:17:02]
the user input and then return that data. And that would totally work. So. So yeah you
[00:17:08]
can do that and it supports that simpler mental model of just running a script until you're
[00:17:15]
done. The thing that's that I find really fun about back end tasks is that like it is
[00:17:22]
this it's a type it's data. It's a description of an effect or of something to achieve and
[00:17:29]
exactly get out of it. Exactly. And so because it's this sort of declarative description
[00:17:35]
of an effect and how to respond to subsequent effects you can use it in a lot of different
[00:17:42]
places. So like Elm pages scripts is one place you could use it. You know if you wanted to
[00:17:49]
define some sort of runtime where you say oh yeah well you can turn that into you know
[00:17:56]
not an Elm command but something like an Elm command and you return or like you can return
[00:18:02]
a back end task in your in a tuple with a model change and a back end task or like it's
[00:18:09]
possible to do that. And I think that sometimes people underestimate what you can model for
[00:18:16]
frameworks to be able to do like effectful things using this pattern of describing effects
[00:18:24]
as data. I think it's like it's actually a very powerful tool that we can do a lot with
[00:18:29]
and as framework designers we can put guardrails so it's very very clear what what it's possible
[00:18:37]
to do using those data types and where they can be used and where they cannot be used.
[00:18:42]
So you know it's essentially the idea of a managed effect where like calling a back end
[00:18:49]
creating a back end task in Elm pages doesn't do anything. You can create a back end task
[00:18:55]
just like you can create a command but when you give it to Elm pages in a place where
[00:19:01]
it accepts that type then it lets the framework do something with it. So the sky's the limit
[00:19:06]
with how you build things with that. What I really like about this pattern is that because
[00:19:11]
there's an abstraction layer because you make a new API that whose internals are hidden.
[00:19:18]
I mean I'm guessing they're opaque right. Well that decouples you from how it's implemented
[00:19:23]
under the hood. So however you implement back end tasks under the hood or how you implement
[00:19:30]
logging, reading a file, writing to a file, all those things. I'm guessing they're implemented
[00:19:37]
in Node.js at the moment but they could be implemented using a bash script or whatever.
[00:19:44]
And all those options are available as long as you don't tie it to a specific implementation
[00:19:51]
which because there's an abstraction layer they are not. So that is something that I
[00:19:56]
really like is that you're free to implement it however you want, you as the framework
[00:20:02]
author. But I'm guessing if you do it through a port a user can do so as well. Although
[00:20:08]
it is going to be executed through JavaScript. But it could be done through other means maybe
[00:20:14]
Absolutely. Well yeah as you say I mean at the end of the day Elm pages is creating,
[00:20:21]
it's scaffolding up an application around your application. That's sort of what a framework
[00:20:26]
is. And so it's at the end of the day compiling an Elm application and executing it in this
[00:20:35]
case in Node.js. But it could be executing it in other contexts. It could be executing
[00:20:39]
it with Deno or Cloudflare workers or with Bun with different run times. But at the end
[00:20:48]
of the day it is using Elm which its way of communicating is through ports. And so it's
[00:20:54]
just it's just building that. It's just like a little application. You know just like when
[00:20:59]
we write Elm review and Elm GraphQL command line tools that you npm install somewhere
[00:21:06]
in there where we have compiled Elm code where we take that JS we import that code and run
[00:21:14]
it set up you know in it the Elm application subscribe to some ports send back some ports.
[00:21:21]
So we're communicating to the Elm app through ports and that's that's all that Elm pages
[00:21:25]
is doing. But it creates a set of abstractions for that that makes it easier for the user
[00:21:31]
to basically execute things in a back end and run a script in a back end context which
[00:21:38]
turns out is a very useful thing to do if you're you know making a static site because
[00:21:46]
you want to read some files and then you want to pull that data in your front end. But that's
[00:21:51]
also scripting right. So it does bring up the question like is Elm a good tool for this
[00:21:58]
type of task like this kind of back end task. Right. Yeah exactly. Is Elm a good tool for
[00:22:07]
writing a script. Is that a good idea. And I mean of course we're biased. We want to
[00:22:13]
do everything in Elm and we write lots and lots of Node.js code so that we can have the
[00:22:20]
ability to do things only in Elm. But I think it's I think it's quite nice to be able to
[00:22:26]
just operate within the confines of type safe Elm code where you can write a JSON decoder
[00:22:33]
and have have things fail and have this explicitness. But you can write to a file. You can log you
[00:22:41]
can read files which by the way like writing to a file is like a built in thing in the
[00:22:47]
script module that Elm pages provides. But you can define your own custom back end tasks
[00:22:53]
as well. So it's just a way of binding Elm code and these back end tasks to a back end.
[00:22:59]
That's that's really what a back end task is. OK. So you mentioned doing this in Elm
[00:23:04]
or doing this in other languages or tools. So yeah. Like does it make sense to write
[00:23:12]
a script in Elm or in Elm pages or is it sometimes better to do it in JavaScript or Bash or Python
[00:23:20]
or whatever. So you say that there's at the moment logging there's writing a file. That's
[00:23:27]
not a lot of things that you can do built out of the box but you can do more through
[00:23:33]
custom back end tasks using ports. So basically you can probably do anything that a JavaScript
[00:23:40]
script could do. But is it what are the gains what are the benefits that you have when you
[00:23:46]
do it through Elm pages compared to just running a Node.js script for instance.
[00:23:52]
Exactly. Yeah. Great question. And that's that's exactly the right question I think.
[00:23:57]
So first of all a little bit of background. The motivation for Elm pages scripts and people
[00:24:02]
might be asking like Elm pages scripts like why what does Elm pages have to do with scripts.
[00:24:09]
Yeah the name don't match. Right. At the moment. So the Elm pages script was born out of this
[00:24:18]
use case of generating like the scaffolding for a new route. So Elm pages v2 has an Elm
[00:24:25]
pages add command so you can say Elm pages add blog dot slug underscore and it generates
[00:24:33]
something for a route where it's blog slash some dynamics slug. And I wanted to have a
[00:24:40]
way to let users customize that. Ryan has created a nice feature in Elm SPA where you
[00:24:48]
can do some templating and create custom commands for for scaffolding new pages. I was really
[00:24:56]
keen on on using Matt's Elm code gen tool for that. And so as I was starting to build
[00:25:03]
that I'm like well it would be really nice if if I could use Elm code gen to create scaffolding
[00:25:11]
for new routes. But I also want to be able to read an environment variable read some
[00:25:17]
configuration from a JSON file maybe get some like JSON data from an API to figure out how
[00:25:25]
I'm going to generate my my new routes. And so well that's kind of what back end tasks
[00:25:32]
let you do. So I wanted I knew for a long time that I wanted to have the ability to
[00:25:37]
use back end tasks to scaffold new routes because I just really like this abstraction
[00:25:43]
of back end tasks and I want to use it for a lot of things. And then it's like well if
[00:25:48]
I can like once I've built that it's like well this is no longer a scaffolding tool.
[00:25:53]
This is just a way to like run back end tasks by like writing a module that defines a back
[00:26:00]
end task to run. And OK maybe the special case is it's like writing a file in a specific
[00:26:06]
format but why not just give a back end task to write a file and then you can use that.
[00:26:12]
And now it's just on pages scripts. So it's really like Ruby on Rails generators where
[00:26:18]
it's just like that was the main motivation was Ruby on Rails generators are used for
[00:26:25]
if you want to create a new page with a form and then you just it's a tool for very quickly
[00:26:31]
building up boilerplate. So it's like you know you create a new controller in Rails
[00:26:37]
and your template and your template is defining a form and your form has these fields and
[00:26:42]
you also want to create you know some some stuff for for working with active record to
[00:26:50]
define this new user model or whatever. And so people are very productive using Rails
[00:26:56]
generators where they'll say like Rails generate whatever and and you can build custom workflows.
[00:27:03]
You can build custom generators. You can even install custom generators. So and then they
[00:27:07]
say that Elm has a lot of boilerplates like we don't write write scripts for that usually.
[00:27:12]
Right. Right. But I wanted a way to customize template templates for for adding new routes.
[00:27:20]
And also you know if you want to create a new page and be super productive where you
[00:27:25]
can say hey I'm going to make a new form and it has these fields. Why not be able to write
[00:27:30]
a custom generator a custom Elm pages script that lets you just template that. And if you
[00:27:36]
want to read some configuration from something or whatever you want to do why not let users
[00:27:41]
do that. So that was the motivation. Now back to the question of like why what what benefit
[00:27:46]
do you gain by doing this compared to a bash script or a node script. If we look at the
[00:27:52]
pros and cons between like writing a script in in Elm and writing a script in bash or
[00:27:58]
Node.js we can see some pretty pretty obvious pros and cons on either side. So let's look
[00:28:05]
at like writing a vanilla Elm script. If we were to do it on our own we would need to
[00:28:13]
compile we would you know we'd need to like compile some Elm script we would need to take
[00:28:19]
that compiled Elm script and import it into Node.js so we could run it and do some boilerplate
[00:28:24]
around that. And obviously that's not great. And Elm pages scripts takes care of that for
[00:28:29]
you. So we no longer have to worry about that. But what if we just wanted to grab some HTTP
[00:28:37]
data. Right. If we have to create and update to do that that becomes pretty verbose and
[00:28:42]
tedious. So back end tasks make that less tedious because you just do back end tasks
[00:28:48]
dot HTTP dot get JSON URL JSON decoder and then you can do back end tasks dot and then
[00:28:56]
you don't have that boilerplate of init update subscriptions. Exactly. Now the other thing
[00:29:02]
that becomes tedious there is dealing with failure. Right. So in Elm everything is very
[00:29:08]
explicit when things can go wrong and you have to painstakingly handle every possible
[00:29:13]
error. What if the decoded value does not successfully decode to the format you expected.
[00:29:20]
What if there's an HTTP error. What if there's a file reading error. All these things you
[00:29:25]
have to painstakingly handle every possible failure. So compare that with writing a script
[00:29:30]
in bash or node. The challenge is well what what things can fail. So it's very easy to
[00:29:38]
just run something and let it fail. Right. It just throws an exception. The problem is
[00:29:44]
knowing where it might fail and what implicit assumptions there are and what possible runtime
[00:29:52]
errors are lurking there. So if you want to write a quick and dirty script and you just
[00:29:56]
say I want to hit this API I want to grab this data I want to map the data a little
[00:30:01]
bit and I want to write some file or something like that. Right. Then writing a Node.js script
[00:30:07]
is is great for that because it doesn't get in your way with saying hey the errors might
[00:30:13]
be wrong. You just pull off JSON data. It doesn't get in your way with saying hey this
[00:30:17]
HTTP request might fail. So that if you're just writing a vanilla Elm file you do have
[00:30:24]
to deal with those cases and that becomes tedious. Elm Elm pages back end tasks try
[00:30:30]
to address that problem. So now with with data sources and Elm pages v2 it was more
[00:30:39]
like we talked about earlier it was more convenient because you can just let things fail but it
[00:30:44]
was less powerful because you couldn't handle possible failures or see where it was possible
[00:30:49]
for something to fail. So it was less safe and less powerful in v3 it's more safe and
[00:30:54]
powerful. It's a little a little more verbose. So Elm pages v3 provides a new abstraction
[00:31:03]
called a fatal error. And this is very important for the ergonomics of being able to do a quick
[00:31:10]
and dirty task but it tries to achieve a balance between convenience and safety. So the way
[00:31:18]
it works is it allows you to just give a fatal error to two Elm pages and it will just stop
[00:31:26]
and report the error. So in the case of a script it will just print out what went wrong.
[00:31:32]
So if you say backend task dot HTTP dot get JSON and it gives you a 404 error what you
[00:31:39]
can do it you can do backend task dot allow fatal and that is going to take your HTTP
[00:31:47]
backend task and and give you a backend task fatal error your decoded data. So that fatal
[00:31:55]
error contains the the information that the Elm pages framework needs to print an error
[00:32:02]
message describing what went wrong. So if you do Elm pages run hello and then you hit
[00:32:08]
your API with allow fatal it's just going to print an error message saying hey I was
[00:32:13]
running this HTTP backend task something went wrong. There was a 404 error and because you
[00:32:20]
you opted out of handling and recovering from that error.
[00:32:25]
So what happens if you don't write allow fatal.
[00:32:28]
If you don't write allow fatal in the case of backend task dot HTTP dot get JSON then
[00:32:35]
the types just won't line up because the error type in that get JSON function returns a backend
[00:32:42]
task with with the error having a fatal error and a recoverable error data. So if you wanted
[00:32:50]
to recover from it then you can do backend task dot map error and then you can pull off
[00:32:57]
that recoverable data from that record which is going to be a nice structured HTTP error
[00:33:03]
which could be your JSON decoding error it could be bad body timeout. And so if you want
[00:33:09]
to say if it's a timeout try it again you can do that. You can do backend task on error
[00:33:17]
case error dot recoverable timeout try again or if it is you know whatever else and for
[00:33:25]
all of those different cases that structured data you can choose explicitly how to handle
[00:33:30]
it.
[00:33:31]
So if you don't write allow fatal then you have to do something. And what is that something
[00:33:36]
that you have to do.
[00:33:38]
If you don't. So the at the end of the day the Elm pages expects when you say script
[00:33:46]
dot without CLI options and you give it a backend task the type of that backend task
[00:33:52]
needs to be the error type can be a fatal error and the data type needs to be unit.
[00:33:59]
So so at the end of the day you you need to give it either no possibility of an error
[00:34:05]
or a fatal error if anything. So doing allow fatal just throws away that recoverable error
[00:34:14]
data that has the nicely structured error whereas allow fit. Yeah allow fatal just grabs
[00:34:19]
that fatal error and passes it through. But if you do on error then you can continue with
[00:34:26]
something else.
[00:34:27]
OK. So you have to transform the error type in a way that will print an error or succeed
[00:34:33]
I guess. And if you don't then you have to write to use allow fatal.
[00:34:40]
Right. At the end of the day that's the type of error that you can give it. So you can't
[00:34:45]
you can't just give obviously any error type to Elm pages and have it do something because
[00:34:51]
Elm doesn't let you have like variable return types for something. It's like so it needs
[00:34:57]
to be returning back in task fatal error and you can and unit. And so you could you could
[00:35:05]
define a back end task that has whatever error type just like you know a regular Elm task
[00:35:12]
have an error type. You can map that error type you can do task dot map error. You can
[00:35:16]
also do back and test that map error. It's the same thing whatever the error type is
[00:35:20]
along the way doesn't concern Elm pages. You can you can do whatever you want. You can
[00:35:25]
have whatever structured error data. It's just that if you have the possibility of a
[00:35:31]
failure you have to turn that error type into a fatal error at the end of the day. Right.
[00:35:37]
OK. So basically the fatal error concept in Elm pages is a way of saying hey let's have
[00:35:46]
safety. Let's have a balance between safety and convenience because for the sake of safety
[00:35:53]
we could. So in the design of this I could have just had a single type variable for the
[00:35:59]
data not had an error type variable and just let you say oh yeah it can fail or I want
[00:36:06]
to recover from the failure. What I wanted to have was I wanted to make it very explicit
[00:36:12]
where failures happen and if there's no error type variable there's no possibility of failure.
[00:36:18]
So you can tell just by looking at the types if it's possible for something to fail or
[00:36:22]
not and how it could fail. Now the fatal error type is a very generic failure that doesn't
[00:36:28]
contain any useful information for you. So it's just saying that's the that's the balance
[00:36:33]
between the safety and the convenience. You know it can fail but you can't do anything
[00:36:37]
meaningful to recover from it at that point because you have to kind of choose when you
[00:36:41]
get that data. So that's so the core APIs and Elm pages like HTTP reading from files
[00:36:48]
writing to files things that can fail. They give you these two different bits of data
[00:36:53]
where you can choose I want to either recover or let the fatal exception through the fatal
[00:36:59]
error through. So the point of that design is that you can have the convenience of just
[00:37:06]
saying yeah just give this message to the framework and let it fail or you can recover
[00:37:12]
from it. So it's trying to give you an ergonomic way to easily just say I don't care about
[00:37:17]
this error or a way to recover from it while knowing explicitly whether failure is possible
[00:37:23]
just based on the types. Right. And at the end of the script if it fails then it's always
[00:37:29]
going to have some kind of nice error message or a reasonably nice error message I'm hoping.
[00:37:36]
Oh yeah. I mean that's a major goal of Elm pages for sure is to you know strive for quality
[00:37:42]
we expect in Elm community for error messages. OK. So what I like about this is that you
[00:37:48]
said as you say like you can identify what is going to succeed and what is what can fail.
[00:37:56]
So once you so once this script is compiled by Elm pages if it compiles then it's either
[00:38:04]
going to fail in the intended way or well in a intended way in a or it's going to succeed
[00:38:13]
in the intended way. Yeah. But it's never going to fail because of how you wrote the
[00:38:18]
code. So something that happens a lot to people at least to me but I'm guessing to a lot of
[00:38:24]
people who write scripts is that the script is going to fail because you did something
[00:38:29]
stupid in your script like for instance you write a Node.js script and you mistyped a
[00:38:34]
function name. So that's not going to happen anymore. The only thing the only reason that
[00:38:38]
is going to fail is because some operation that's touched the external system like the
[00:38:46]
file system or made requests across HTTP failed for some reason. But it's never going to fail
[00:38:53]
because of how you wrote the code. So that is quite nice. So that is one of the plus
[00:38:58]
sides that I find in using Elm pages scripts. But do you see other ones compared to writing
[00:39:06]
because you compared it previously with writing a script in Elm without Elm pages which yeah
[00:39:12]
sounds painful. Some people have done it. It's actually not that bad in practice. I
[00:39:17]
have done so myself obviously. But how does it compare to writing something in JavaScript
[00:39:22]
or in Bash or Perl or Python or whatever. When would you do one of those or when would
[00:39:28]
you use Elm pages scripts. Right. Yeah. So you know there's there's a tradeoff in that
[00:39:34]
you know again in the context of Bash or Node.js you don't you don't know where possible failures
[00:39:40]
lurk because you know even if you're writing a script in TypeScript you don't know where
[00:39:47]
you might have gotten some any data back that is actually leaking possibly incorrect type
[00:39:53]
data somewhere. You don't know. So you for me if I'm trying to solve a problem for example
[00:40:02]
like recently I was trying to I was writing a script for for Elm radio where I can automatically
[00:40:12]
apply the right ID3 tags to MP3s that we publish that will apply the right album cover image
[00:40:20]
which you need to do before publishing and the right track information and it helps pull
[00:40:26]
in data from the Notion API so it can get title information and the number of the episode
[00:40:33]
and things like that. And writing it in Node.js I found really frustrating because I even
[00:40:40]
though I was even using like an NPM package for hitting the Notion API but there were
[00:40:45]
all these incorrect assumptions about the format of the JSON data I was getting back
[00:40:51]
even so with this helper package. NPM helper package? Yeah. And you know if I was writing
[00:40:59]
writing in Elm it would be JSON decoders so I would I would immediately turn it into nicely
[00:41:05]
structured data or an error and and be able to get like a shorter feedback cycle as I
[00:41:10]
was working on that script instead of just having to like run it a little bit further
[00:41:15]
run it a little bit further it would just tell me that I have decoding errors until
[00:41:20]
I've gotten the data format as expected which is my preferred workflow. And also I just
[00:41:26]
I can once like if you write a script in Node.js and then it succeeds you're like okay well
[00:41:35]
it's possible for this script to succeed but you're not necessarily convinced that it will
[00:41:39]
succeed for all cases. Whereas like if I if I write the script in Elm I would be much
[00:41:45]
more confident that like oh yeah it it's good now like it's it's handling the expected JSON
[00:41:52]
data I mean maybe the API sends slightly different data formats in different cases but I'm much
[00:41:57]
more confident that I'm done at that point. So that's that's one thing is that confidence
[00:42:04]
which I still want when I'm writing a script like and it's I still want to pull API data
[00:42:10]
down and have some sanity around that being confident that the data format I'm getting
[00:42:16]
is right I still want to work with nice types while I'm doing that and and know that the
[00:42:21]
types I'm working with are correct not like half correct mixed in with some anys that
[00:42:26]
trickled into my system. I mean you don't have anys in bash. Right. Oh man working with
[00:42:35]
the API data responses in bash does not sound fun. I don't even know how you would do that.
[00:42:42]
Yeah. I would just curl it and yeah pray that it works. JQ or something I don't know there
[00:42:49]
yeah there are tools but it's it's not fun you know so it's it's nice to use like a programming
[00:42:54]
language for that not just a bash script. But yes so like the other thing is if if I
[00:43:00]
want to make it more robust to run this script maybe it's like when you write a quick and
[00:43:06]
dirty script you want to just allow failures to just happen right. That would be like in
[00:43:11]
Node.js you just don't do a try catch. So with Elm pages back end tasks you do need
[00:43:17]
to be explicit where failures are possible. But at the same time I mean you know you do
[00:43:23]
your get JSON and then a failure is possible. So the types will not fit together unless
[00:43:30]
you do allow fatal back and test out allow fatal. And yes you do have to write that explicitly
[00:43:36]
but that's all you do. And if if you just say hey I don't want to deal with any possible
[00:43:41]
errors I just want to work with the happy path I expect everything to work. And if if
[00:43:45]
anything goes wrong just give me an error message right. Then you just any time the
[00:43:50]
types tell you to you just do back and test dot allow fatal. And now what you end up with
[00:43:56]
is yes you had to write allow fatal a handful of places. But for one thing you can look
[00:44:02]
at it and see where can fatal things happen. Right. Yeah. That's nice. Yeah. And if you
[00:44:08]
want to recover from it at some point later when you have more time or you want to print
[00:44:14]
out a nicer error message then you know where to look. Exactly. And you know exactly the
[00:44:20]
possible failures that can happen. Like it always feels like uncomfortable for me doing
[00:44:26]
like a try catch in Node.js and then just like expecting the cut exception to be this
[00:44:32]
thing that has this key but then like it might not. And like do I do a try catch within my
[00:44:39]
catch in case my expectations about the properties on that on that error are incorrect. Like
[00:44:48]
so it's if you want to do error handling error recovery like you have nice types that let
[00:44:54]
you do that. If you don't it's explicit where you're not doing that. And that's a very intentional
[00:44:59]
design trade off that it's a little less convenient but it feels more safe. So to me that is a
[00:45:06]
trade off I'm willing to make to write allow fatal a few extra places and have that explicit
[00:45:12]
this and know where things can fail. And then if I want to make my script more robust over
[00:45:18]
time and say like OK this error case I should really have proper error handling for this.
[00:45:23]
This this script that I'm running fails on Sundays because this thing happens and I should
[00:45:28]
really clean that up and add proper error handling. You can or if you want to present
[00:45:35]
nicer error message in one case instead of just saying I got this HTTP error you could
[00:45:43]
give a more custom error message. You can do that. You don't like it. No ends. Right.
[00:45:51]
So you could you could say like instead of saying you know and you know or instead of
[00:45:58]
saying the default can't read file error message that the that it gives you when you get that
[00:46:03]
fatal error in pages in the core APIs you could have you could turn that into your custom
[00:46:11]
error with nice error feedback whereas doing that in Node.js it's just going to be a lot
[00:46:15]
harder. So I just feel like it like the goal of this design is to give you a way to be
[00:46:23]
productive build things up with minimal boilerplate. You write your script hello.elm you expose
[00:46:31]
run its type of script you define a back end task and then you want a quick and dirty script
[00:46:37]
just the happy path you allow fatal. But as you want to deal with more error cases in
[00:46:45]
a graceful way it gives you the tools to do that and to really maintain it. So it's trying
[00:46:51]
to give a balance between convenience and maintainability. So I don't know like would
[00:46:59]
you use Node.js in some cases instead of using an Elm pages script. I'm sure there are cases
[00:47:06]
for that but I think if I if I have some little scripts for like helping with the Elm radio
[00:47:14]
publishing process like I want that in an Elm pages script because I want I want that
[00:47:19]
in Elm and I know. Yeah. Yeah. That makes sense. I mean you could just start writing
[00:47:26]
a script in Node.js like because you start small you do like one thing because it's a
[00:47:34]
prototype and then well you need one additional thing and then you need another additional
[00:47:40]
thing and two three seven additional things and. Right. And at some point you think you
[00:47:50]
should rewrite this in another language. Yeah. Let's rewrite this in bash. This makes a lot
[00:47:56]
more sense. Perfect. Yeah. I would say for me like I feel like when I hit dealing with
[00:48:05]
JSON data in Node.js that's when I really want to just use Elm for that. And I know
[00:48:12]
there are tools like Zod to help you do it in a more Elm way where you're writing things
[00:48:16]
in the style of a decoder. But I don't know for me I'm I'm going to tend to reach for
[00:48:22]
Elm to do that type of task. And if you as you say you can you can start something in
[00:48:27]
Node.js you can create custom back end tasks so you can like write whole chunks of Node.js
[00:48:34]
code in your custom back end task dot TS file and then you can just execute that as a back
[00:48:41]
end task. Yeah. So you can easily migrate from one to the other is what you're saying.
[00:48:45]
Yeah exactly. So the so the custom back end tasks the way you define them you write your
[00:48:52]
custom back end task TS file or JS whatever you prefer. It transpiles it using ES build
[00:48:58]
and and you export async functions and then you do back end task dot custom dot run. You
[00:49:06]
give it the name of the function that you exported from that TypeScript file. You encode
[00:49:12]
some JSON data to pass in. You give it a JSON decoder and then you've got a back end task.
[00:49:19]
And does Elm pages make sure that that port exists both in JavaScript and in Elm before
[00:49:26]
you run it. So it it doesn't need to make sure the port exists in Elm because it's not
[00:49:33]
actually it's it it's using it's not defining a port for each of those but it's just calling
[00:49:39]
your async function. But it does make sure that your your custom back end task TypeScript
[00:49:47]
file compiles. If there is a an error in the file you can recover from that as one as the
[00:49:54]
recoverable error type for that back end task or if you allow fatal it'll print it out in
[00:49:59]
a nice formatted way. Yeah. If you do not have an exported function of the name that
[00:50:06]
you're trying to call it gives you that as part of the structured error type. And if
[00:50:12]
you export something but it's not a function it even tells you about that. So all of those
[00:50:17]
possible error variants will be automatically printed for you in a nice format if you allow
[00:50:23]
fatal and if you want to recover from it you can even do that like it even has a custom
[00:50:29]
type with all those possible failure cases for you. Yeah. So you can pat a match on it
[00:50:35]
and print out a nice error message or something. Exactly. And it will also if you throw an
[00:50:40]
exception in your port data source it will give you if you throw JSON data it will give
[00:50:49]
you that as the error type. OK. So now another topic I do find it a little bit weird that
[00:50:59]
to run a script in Elm which I would love to do and I don't mind necessarily writing
[00:51:05]
a scripts folder with an Elm JSON file and all those boilerplate things. But do I really
[00:51:12]
have to pull in all of Elm pages. Right. Well I mean how do I explain it to my co-worker
[00:51:19]
like oh yeah of course use Elm pages. The name makes a lot of sense. Why not Elm scripts
[00:51:24]
or. Right. No I mean that's fair. So you can you know you can create a script folder in
[00:51:33]
any project doesn't need any of the Elm pages boilerplate. And you know could it someday
[00:51:39]
make sense to have maybe slimmed down version of the NPM package with a different name.
[00:51:47]
Sure. But right now that's not a priority right now it's like I mean right now it is
[00:51:52]
a tool that's basically Rails generate for Elm pages. So it's a tool for helping Elm
[00:51:59]
pages users be more productive. And it happens to be usable outside of an Elm pages project.
[00:52:05]
But yeah it's definitely like a little funky. The thing is like the concept of a back end
[00:52:11]
task is so tied to Elm pages right now. To Elm pages implementation you mean or. Because
[00:52:18]
as you say it like doesn't have anything to do with Elm pages necessarily. It just happens
[00:52:23]
to be code that is in that project also for good reasons. The use case of Elm pages but
[00:52:29]
it doesn't have to be. Right. Yeah. The problem is like I've had this also for Elm review
[00:52:37]
is like where do you draw the line. Like does it make sense to have Elm have the scripts
[00:52:45]
parts in a separate CLI in a separate Elm package called Elm scripts or whatever which
[00:52:52]
you would then use in Elm pages. But that adds a lot of complexity about how do you
[00:52:57]
make sure that those are in sync and how do you handle some of the underlying things that
[00:53:02]
have to be written in JavaScript or have to be written to something. So yeah it's a it's
[00:53:09]
a bit annoying but also yeah. Yeah it feels a little funky but like the fact that you're
[00:53:15]
calling Elm pages run in a project that is not an Elm pages project is like the main
[00:53:20]
problem. And in that case like make an alias to solve that problem. But like in the future
[00:53:29]
it definitely could be reasonable to have like you know like something called Elm engine
[00:53:36]
or Elm back end task or Elm back end or something and have a package for that have fatal error
[00:53:43]
and back end task and the things related to that concept exist there in that separate
[00:53:49]
thing. And then at that point actually it could be could be cool because potentially
[00:53:55]
I could make it like a standalone thing for resolving a back end task where the code to
[00:54:01]
take something of that back end task type and execute that and then give you the resolved
[00:54:07]
data could be like split off into something and then Elm review could let you use a back
[00:54:13]
end task in some place or whichever tool. So I mean I would I am interested in like
[00:54:19]
being being able to access arbitrary files. Right. Exactly. Maybe not HTTP but I mean
[00:54:26]
I could make that limitation. So yeah that could be interesting. It probably wouldn't
[00:54:31]
work that way but maybe under the hood. Right. So that's yeah it's definitely something that
[00:54:37]
could happen in the future. For now I'm really keen to see like what people build with it
[00:54:42]
and and go from there. But yeah you could definitely imagine a possible future where
[00:54:47]
it's sort of designed to fit into more places and I would love to see people using it for
[00:54:51]
more types of tasks. Yeah. If you split it off then the only thing that you gain is ergonomics
[00:54:58]
I'm guessing because it's not going to be necessarily faster. Definitely one use case
[00:55:04]
I see for for these scripts is if you want to work with existing data that you have in
[00:55:10]
your own pages projects. Right. For instance it's used on the Elm Radio website to fetch
[00:55:16]
episodes. Right. Episode data. Well now if you want to generate something you want to
[00:55:25]
generate a file containing the list of episodes while you just reuse those same back end tasks.
[00:55:32]
So that is really nice I think. Exactly. Yeah. For like generating our transcripts where
[00:55:36]
there are a set of things that don't have transcripts yet that could just be an Elm
[00:55:40]
pages script because right now there's a back end task that goes and looks at the file system
[00:55:47]
and decodes a bunch of front matter from files and all these things that you can do with
[00:55:51]
Elm pages back end tasks and it figures out the list of episodes which are which exist
[00:55:58]
but don't yet have transcript data. So we could tie that in in an Elm pages script with
[00:56:04]
actually just run the script and it goes and executes the transcripts for generating transcripts
[00:56:11]
that you need and moving the files to the appropriate file locations and all that. So
[00:56:16]
you mentioned before that this is mostly used for running scripts on your own computer right.
[00:56:23]
I know that Elm pages also has support for serverless or all those kinds of things that
[00:56:29]
I have to admit I don't understand too much. But would this be usable for serverless things
[00:56:36]
as well or would that be different parts of Elm pages in which case we will talk about
[00:56:42]
it in a later episode. Yeah. So yes and no. So it wouldn't be script but back end tasks
[00:56:49]
can be resolved in serverless functions or on a server and that that's what server rendered
[00:56:55]
routes are in Elm pages v3. It uses back end tasks you can do the same types of things
[00:57:00]
but for scripts there's no reason why you wouldn't use an Elm pages script in your CI.
[00:57:09]
So like you should you should absolutely use it like outside of your local machine. And
[00:57:14]
again like it's designed to be relatively easy to write a quick and dirty script that
[00:57:20]
only handles the happy path and then mature into a script with nice error handling and
[00:57:28]
and be a really robust script. So like I think it's a great tool for like writing team scripts
[00:57:34]
and maintaining them and having them on your CI and making them really robust over time.
[00:57:40]
So one thing that we haven't touched on yet that I want to make sure we mention is the
[00:57:45]
CLI options. So Elm pages scripts have the ability to to include a CLI options parser.
[00:57:54]
So for anyone who hasn't heard the term CLI options it's just the term for you know running
[00:58:01]
Elm review dash dash fix dash all that would be a command line option that's specifically
[00:58:09]
a keyword option but is it right. Sorry that one is called a flag. Yeah that was a what
[00:58:15]
was I thinking. Yeah. But yeah all the things that you can provide are options I'm guessing
[00:58:21]
and the things that start with a dash or dash dash are flags. Is that it. So the ones that
[00:58:28]
do not that only have a key but not a value are called flags. The ones that have a key
[00:58:33]
and value are. So I looked through a lot of different names for these terms and based
[00:58:43]
on common conventions and the the ones that were widely used and seemed like the most
[00:58:49]
intuitive I I came up with a little label. So we'll link to my Elm CLI options parser
[00:58:57]
package. This is actually what Elm CLI or what Elm pages scripts uses to parse command
[00:59:03]
line options. But there's a little graphic in there that has little annotations of what
[00:59:10]
these parts of a command line call are. But yeah so a flag does not have a value. You
[00:59:16]
know if you write log dash dash stat. Yeah it's a Boolean in a way. Exactly exactly.
[00:59:23]
It's going to give you a Boolean and then you have keyword ones. You can mix up the
[00:59:27]
order of those ones and it's order independent. You have positional arguments. You can have
[00:59:33]
optional positional arguments. So Elm CLI options parser is an Elm package that I built.
[00:59:38]
I use it for Elm GraphQL. I've used it for years in Elm GraphQL and it turns a command
[00:59:46]
line command into structured data or an error message that tells you the help options of
[00:59:54]
what went wrong and why the command was not valid. So in Elm pages scripts if you want
[01:00:00]
to you can accept command line arguments. So our hello world we said script dot without
[01:00:07]
CLI options. But if you and that just takes a back end task and that's it. So script dot
[01:00:14]
log hello world. That's it. Script dot without CLI options script dot log hello. If you want
[01:00:21]
you can accept CLI options. So that would be script dot with CLI options. Then you give
[01:00:27]
it your CLI options parser and then you receive that parsed data and return a back end task.
[01:00:36]
So you could you know based on based on a flag do one type of back end task or another.
[01:00:43]
Yeah that makes a lot of sense. Yeah. So just another sort of like essentially if you think
[01:00:50]
about it if you if you want to do a simple scripting workflow you know in in bash you
[01:00:56]
can just pull off positional arguments in node JS you read a bunch of stack overflow
[01:01:04]
questions until you figure out the right incantation and which like which array index the actual
[01:01:12]
user arguments start at and then where to get those. Yeah you mean until you learn which
[01:01:21]
command line tool you have to use like the use commander or minimists or no not that
[01:01:27]
one because it's deprecated or that other one has this problem with duplicate flags
[01:01:33]
or exactly that stage to that stage. But stage one is just like oh wait the index zero of
[01:01:42]
the arguments of the process dot argv or whatever is like the command that was called and then
[01:01:51]
yeah the second one is whatever. And so yeah after you like finally figure that out and
[01:01:59]
you pull a single argument because that's all you need and then you realize oh I actually
[01:02:04]
need to parse different types of options and then you then you go through and figure out
[01:02:09]
which of the NPM packages is cool for that now. And of course it's like Elm is really
[01:02:15]
good for turning unstructured data into structured data. It's like parse don't validate is what
[01:02:21]
makes Elm awesome to me I think among other things. But it really shines there whereas
[01:02:29]
if you're using minimist or commander or whatever it's just not as nice to work with massaging
[01:02:36]
these things into nicely structured data. So yeah I find that that's like a really nice
[01:02:42]
workflow because in Elm pages script like it comes built in with this tool. You just
[01:02:49]
define your command line options parser and like you sort of know the data you're going
[01:02:55]
to end up with and it has built it. It's all wired in for you. So it has a baked in opinion
[01:03:01]
about that. So again that's the philosophy is like trying to remove friction as much
[01:03:07]
as possible while still giving you like tools for doing things in a powerful but safe way.
[01:03:14]
And I think this this fits in with that where like I don't know I just I feel like it's
[01:03:20]
prohibitively expensive to actually figure out how to build command line options parsing
[01:03:26]
for a quick and dirty script in a lot of cases. But when I'm working with this I don't I don't
[01:03:31]
feel that I feel like I should ask because there are alternatives to running.
[01:03:37]
Scripts in Elm. I think the most known one is Elm POSIX. There's also ElmScript which
[01:03:45]
is a name we've used unknowingly so far. At least I did. So ElmScript from Ian McKenzie
[01:03:51]
and ElmPOSIX from Albert Dahlin. Have you used those for inspiration? Have you seen
[01:04:00]
limitations of those or is it just that well it made sense for Elm pages and this is just
[01:04:06]
an entirely novel approach and API.
[01:04:09]
Right. Yeah I'm I've been aware of those tools but but yeah as you say it's more the latter
[01:04:15]
that it's sort of emerged from the wanting to be able to use back end tasks in different
[01:04:21]
places. And so rather than looking at what's out there how would I do it differently or
[01:04:27]
do I like the way it's done and then designing based on that it was more just I want to be
[01:04:32]
able to use back end tasks. What would that look like. But that said like comparing them
[01:04:37]
like Elm POSIX for example it's it has this IO Monad concept where it makes the tradeoff
[01:04:45]
of having a single type variable for the for the resulting data that you get meaning that
[01:04:52]
errors are not represented in the type which as we talked about is a it's a tradeoff. It's
[01:04:58]
a tradeoff of convenience versus explicitness of possible failures. So it chooses the tradeoff
[01:05:04]
of convenience which is totally reasonable tradeoff for a command line tool. And yeah
[01:05:10]
the Elm POSIX standard API has a lot more functions in the toolkit designed at designed
[01:05:18]
for helping you do sort of scripty tasks like reading the the flags for a file and you know
[01:05:27]
making things writable and things like that. So that's not really the it's it's certainly
[01:05:35]
like a little bit confusing but that's that's not the main purpose of of Elm pages scripts.
[01:05:42]
The main purpose of Elm pages scripts again it's like trying to be like a Rails generator
[01:05:46]
type thing and trying to be a toolkit for helping to manage your project again like
[01:05:53]
helping the publishing process for Elm radio dot com. That's like that's the type of thing
[01:05:59]
it's designed for. You can do whatever you want to with the custom back end tasks but
[01:06:06]
it's not like it wouldn't fit well in Elm pages to have a large API for making directories
[01:06:15]
and making files executable and things like that. So that's not what it focuses on.
[01:06:20]
I don't know. I think it could make sense at least creating directories. Yeah. If you
[01:06:26]
say like this is not what's Elm pages scripts is meant for then you know that people are
[01:06:34]
afraid to use it then in a sense that oh well if this is not what it was meant for then
[01:06:41]
I might use it in a way that was unexpected or not meant for and then Dillon is going to
[01:06:47]
pull the plug and remove those features from me. I mean we've seen this in Elmland so.
[01:06:54]
I don't I don't look at it quite like that. So to me it's more about what exists in the
[01:06:59]
standard API because a back end task gives you a way to define a custom back end task
[01:07:06]
which is just JSON in JSON out. You can you can build anything with that. So if you want
[01:07:12]
to build like the difference is that the standard library in Elm pages does not have a lot of
[01:07:22]
functions for that built in. So but so you know maybe that in the future could be an
[01:07:28]
argument for something like you were describing pulling out a separate thing and maybe having
[01:07:34]
like an extended standard library. It's a difficult challenge of like how you package
[01:07:39]
together Elm code and this like back end code for doing these Node.js things. But you know
[01:07:47]
but potentially you could kind of have the ability to do these things sort of built in
[01:07:53]
somewhere but then not expose the back end task set of functions for for using them by
[01:08:01]
default. There are a number of ways I could imagine that going but.
[01:08:04]
So what I'm hearing is Pinky promise I won't remove things. And I mean it's it's just like
[01:08:12]
a back end task is a general purpose tool. It is not very opinionated. It's like like
[01:08:20]
Elm Elm removed like custom the ability to do user defined custom operators but ports
[01:08:28]
are there. It's not like oh our ports going to stop letting me do whatever it's like no
[01:08:32]
port a port is a port. It's like a general purpose language feature that's like core
[01:08:37]
to the design and it's not going to it's not going to go away. Like it's the same with
[01:08:42]
a back end task like that's just a core concept in in Elm pages and that's not going to go
[01:08:50]
away and that's not going to change like you can you can define your own back end tasks.
[01:08:56]
Right. Yeah. I mostly wanted people to to know what they can rely on without being afraid
[01:09:02]
of like things getting removed. Right. No it's a it's a great point to set expectations
[01:09:08]
there and again the expectation is like it's a totally general purpose building block and
[01:09:14]
you can you can run whatever you need to in your back end task. But the core libraries
[01:09:22]
might not you might not expect the core standard library to expose functions for doing a wide
[01:09:29]
variety of scripting tasks because that's not the main goal. So that's where I went
[01:09:34]
then yourself. Exactly. And that's not going to change. Yeah. All right. OK. Well we are
[01:09:40]
at the end of the script. If I want to make one final pun where can people try this out
[01:09:46]
because Elm pages v3 has not been released but you can already try this out. So how can
[01:09:52]
they try it out. How can they help. And what are you looking for. Yeah. So I will link
[01:09:58]
to a starter repo both in Elm pages starter repo for v3 as well as a minimal boilerplate
[01:10:07]
branch on that repo that gives you the minimum setup for an Elm pages script and also has
[01:10:13]
some information about Elm pages scripts and how to run them. I would I would love to hear
[01:10:18]
about what people do with them. I think it's like a pretty general tool and I think I'll
[01:10:24]
be surprised by some of the use cases people find for this. Other than that yeah the Elm
[01:10:29]
pages docs on pages v3 docs and and the Elm pages channel on Slack is a great place to
[01:10:36]
ask questions. Yeah. So people will not use Elm pages they will use Elm pages v3 alpha.
[01:10:45]
Was that correct. Yeah. There's a package Elm pages v3 beta in hindsight I probably
[01:10:51]
should have called it like Elm pages pre-release or something. But yeah. So and and hopefully
[01:10:59]
it won't be too much longer before that's a stable release. So hopefully that that instruction
[01:11:03]
will be irrelevant soon. But but definitely keep an eye on the docs if it says deprecated
[01:11:08]
this is now a stable release then keep an eye out for that. I did want to mention one
[01:11:14]
more quick thing which is so I think one of the really exciting things that that comes
[01:11:20]
from designing these things you know as as data back end tasks are just a type of data
[01:11:27]
that you pass to a specific place a CLI options parser is just a piece of data. So one interesting
[01:11:34]
thing that comes from that is because it's data you could turn the validator for CLI
[01:11:42]
options into a web interface that presents input fields for all the different flags.
[01:11:49]
So this is one thing on my mind that I think could be a cool project is to have like on
[01:11:55]
the Elm pages dev server or to have maybe some separate command whatever it may be some
[01:12:01]
way to pop up a web page that you can type in your command and get the validation messages
[01:12:08]
because Elm CLI options parser lets you define validations for all of the CLI flags and it
[01:12:14]
enumerates all the possible ways you could define you could run the CLI all the sub commands
[01:12:20]
all of the optional keyword arguments all of the required keyword arguments. So you
[01:12:25]
could have a cool set of like drop downs that tell you exactly what's required and what's
[01:12:30]
optional and give you validation messages in real time as you type them so you can see
[01:12:36]
before you run it what error message you're going to get and you know before you run it
[01:12:41]
if the CLI option parsing will succeed and then you can just hit execute when you're
[01:12:46]
done and it can just run it from that running server it can actually execute the command.
[01:12:51]
So that is one thing that I think just again it's like using these tools that are built
[01:12:58]
around pure functions and data types that describe these things opens up some really
[01:13:03]
cool stuff.
[01:13:04]
Absolutely. Yeah.
[01:13:06]
Another thing on my radar for the future is I think it would be really cool to have a
[01:13:11]
command for bundling a script so you could write a script run this bundle command and
[01:13:16]
get a little optimized script that includes all the command line parsing and everything
[01:13:21]
just a single self-contained node.js file and then you could publish that as an npm
[01:13:27]
package or include it in your in your path somewhere in your bin folder and run it on
[01:13:34]
your system. So yeah lots of lots of fun things that this could take different directions
[01:13:39]
in the future.
[01:13:40]
Yeah I'm excited to hear what people use it for as well.
[01:13:43]
Yeah.
[01:13:44]
Could be fun to use it inside of Elm Review but that would be an additional dependency
[01:13:49]
that I don't think people want.
[01:13:52]
Right. Which is why maybe bundling it could could become interesting in some use cases
[01:13:57]
but that's that's fair.
[01:13:59]
Yeah. Well stay tuned and Jeroen until next time.
[01:14:04]
Until next time.