We discuss the benefits that using the elm-spa framework bring to your codebase, and how to get started.
September 7, 2020

Getting Started Resources


Hello, Jeroen.
Hello, Dillon.
How are you today?
I'm doing very well. How about you?
Very well on my end too.
And what are we talking about today?
And how do we pronounce what we're talking about today, most importantly?
I think we're going to talk about Elmspa...
No, um...
Yeah, let's go with Elmspa.
That sounds better.
Let's put that controversy to bed.
We're just going to go with Elmspa once and for all.
Yeah, even if Elmspa sounds a bit more relaxing.
To some, to some.
What is this Elmspa tool, Jeroen?
Yeah, so Elmspa is a tool that Ryan Haskell Graz made,
which is meant to create single page applications in Elm.
That's what the SBA stands for, single page applications.
So making single page applications is a recurring question
from beginners when they come to the Elm community,
or even the front end community, I guess.
So the canonical example was Elmspa example,
which was made by Richard Feldman.
And Ryan built something else that is called Elmspa,
which helps you to do this.
Right, yeah, and I guess it's worth mentioning that Elm 19
gave us browser.application, which sort of makes it simpler
to build an SBA in just regular Elm.
But still, you have to wire in, essentially,
every single page that you create,
there's a nice API for routing, the Elm routing API.
You route it to a custom type.
You do a case statement on the type of page that you have
based on the route that you parse.
I think of it like a telephone operator,
you know, those old school telephone operators
that kind of take the wires and they route calls manually from,
you know, somebody calls in, then they patch them to the caller.
I have seen them on TV shows, but that's it.
That's what I feel like when I'm writing the top level code
that's sort of delegating to an individual page
from the top level.
And, you know, so you have to wire in every message.
You say if a message comes in, if that message is a homepage message
or if that message is a user settings page message,
then you have to route it to settings page.update.
And same thing for view and same thing for init, whatever.
That's right.
And then there's this kind of thing that always feels a bit odd to me,
which is what happens if you're on the user settings page.
The model type that you have is user settings,
but the message that comes in is a different message.
Then you have to do like a wild card clause
and just throw away that message, which also, to me, it feels too low level.
So that can happen when you're on the homepage, for instance.
And you make a HTTP request.
And before it finishes, you switch pages.
So now you're on the account page and you get that previous message back.
Well, then who should handle that message?
Well, maybe the homepage, but you don't have access to the homepage model anymore.
So that's odd.
So people usually just end up ignoring it.
That's right.
So that's how it was just with vanilla Elm.
You have to do all of this wiring to do an SPA.
So Elm 19 gave us some great helpers to build an SPA,
but there's still a lot of work involved in just wiring things to the right place.
And that said, we're humans.
So if we're doing all of that work to wire things into place ourselves,
there may be some inconsistencies.
There are also certain patterns that tend to emerge.
And you have to make a choice which pattern you're going to use.
There's been some controversy around this sort of Elm taco,
or I guess it has a different...
Elm shared states.
Elm shared state is now the name.
I mean, Elm taco was the name because it was basically like,
let's not give this a real name because it's just an idea or an experiment or something.
And it stuck and people talk about it a lot.
So there are different patterns for managing state in a single page application in Elm.
And there are different ways to...
How do you communicate from a child page to the global state?
If you have like a modal window, some people do like a...
They send back a triplet.
Instead of a tuple, they'll send a triple back that has a model message
and then some sort of thing out message or a thing for the parent for the global state.
Yeah. They have to handle for instance to close a dialogue or something like that.
Yes. Exactly. Yeah.
So that's another thing that Elm SPA gives you is it's just sort of an opinionated pattern
of how you're going to handle that communication between the parent and child pages.
I shouldn't say parent and child pages.
Pages and the global state.
So there's one level.
There's what Ryan in Elm SPA calls a page component, like the pages he calls components.
And then there's the global state and that's it.
There's no like sub children under that.
You can still have sub children for inside pages, but that's different.
Yeah. The rest is left to you.
But as far as Elm SPA is concerned, the only first class concepts that exist are the shared state
or the global state, the page components and that's it.
Yeah. So maybe let's go into how it works.
Mm hmm.
So basically in V5, which we're going to talk about today.
Elm SPA consists of CLI that you can get on the NPM, which is called Elm dash SPA.
And what that project with that tool will do for you is create a sample project.
You don't have to go through that, but you can use it.
And it sets up a few files, including a main file or shared file.
And then some files related to the SPA concepts like document, page and URL.
Yeah. So those are files that is going to make when it creates a project.
And then you can go and modify those if you want.
And then it's going to generate a few files for you.
Basically, it's going to generate those every time you run it.
And it's going to generate the wiring needed for the pages that you create.
Right. And when you say it's going to generate it every time you run it,
the source of truth for the stuff it's generating is the pages folder.
So there's source slash pages, you know, like a lot of different web frameworks
like next JS and like next and these types of tools do file path based routing.
Yeah, which we'll get into. But but the the main point here is that it's going to generate code
based on the Elm modules that you have in source slash pages.
So as you update that, it actually has a watcher that will regenerate the code as you change those things.
Yeah, because you can run it as a server with which runs Elm live under the hood.
So you will have as many pages as you have Elm files in the pages folder and subfolders.
Exactly. So should we talk about the file path based routing a little bit?
Yeah, I think it makes sense.
You know, it's interesting. I think that different browsers have different logic that they use for case sensitivity for URLs.
But in in general, conceptually, I think a URL is in many browsers considered to be case insensitive, I believe.
Yeah, I think so, too. Except maybe like I don't know if query parameters is an exception to that or not.
I don't know too much about the specifics of that spec.
But in Elm S.P.A., you don't get to choose your casing and stuff like that, which I think is is a feature.
And so, you know, if you do a source pages slash home dot Elm, then that's going to give you slash home.
It's going to give you that route with a lowercase h and it uses the camel casing.
It turns that into kebab casing.
Yeah, meaning it's got hyphens in between the words.
Hyphens or dashes? Is that the same thing?
Same thing. OK. Yeah.
Yeah. So it's it's a kebab because you have a skewer. A skewer. Thank you.
Skewer going through the words. Yep. Yep. Yeah. It's a it's a pretty nice, nice metaphor.
I like it. So that's like the basic version.
You can you can nest folders to create sort of more slashes in the route.
What about like dynamic routes?
Yeah. So dynamic routes is sometimes you're going to have URLs like your site dot com slash users slash an ID, a user ID and then maybe slash something.
So you will have a user folder and inside that you would have a route with a dynamic component to it.
Yeah. What you would then do is you name that Elm file or folder if there is a path in it.
Name underscore string or ID underscore string.
So ID is the name of the property that you will have in your pages flags or model.
I think it's probably a flag and string would be the type of that value.
So you can have either strings or ints. Right.
At the moment, I don't know if if you plan to support more, but I think those are pretty safe to start with, at least.
Yeah. And I mean, if you if you want to parse it into some more nuanced structure, then you take the string and you parse it into something and validate it.
Yeah. And you can always show a not found error or something like that if you parse it and it doesn't work out.
Yeah, exactly.
I think it's worth noting for people considering using Elm S.P.A. on a project.
That's something that Elm S.P.A. is opinionated about and goes all in on is this idea that there is a one to one mapping between, you know, your routes and your file structure.
And I think that's quite nice for a couple of reasons.
For one thing, it's nice because you don't have to write that sort of route parser logic.
You know, you do that by the way you structure your code.
But I think more importantly, if somebody is trying to navigate through your code and figure out how the app is structured, where does this page live?
There's a one to one mapping. So in terms of navigating code, it makes that a lot easier because you just look at the URL and you can very quickly navigate to that page.
Yeah. Usually what I do to find which page I need to modify to modify something is I find a string close to where.
Exactly. Exactly.
Yeah, class name or a string and then I copy paste that and find it in my editor.
And usually I find it quite quickly in the project I'm working on.
But exactly. Yeah.
But having this routing be very one to one makes it much easier, I think.
Yeah, I think that's quite nice.
Both between the file path based routing and the sort of opinionated way of structuring an S.P.A. that S.P.A. gives you.
I think it makes it really easy to navigate through an app, whether you're looking at your own S.P.A. that you maintain every single day or looking at somebody else's code that uses the same tool.
It makes it very predictable where things are going to live and how things are going to be structured.
There's no sort of like, oh, well, we were experimenting with this way of sort of having pages for this one part of it handled over here.
And then the update for these other pages comes over here.
And so some of the code is like this and some of the other code is like this.
It's like, no, there's, you know, the top level thing that calls the page that has the init and update and all those things for page is going to be defined in this module.
You know exactly where to find that.
Yeah, you say it's opinionated and yeah, it is.
It's a pretty uncontroversial.
Yeah, it sounds very controversial.
I agree.
Like the only downside I see to that is that if you want to have two pages that look very much alike, like you have a list of users and then when you click on it, you go to the same page with slash that I.D.
But you keep the same layout.
You just see the user details on the right side of the page.
Then you just have to make the components that that contain that are contained in those pages shared.
Yeah, that's pretty much it.
There's not much you have to generalize to make it work.
Yeah, I completely agree.
I think that it's fairly uncontroversial.
It's nice because, you know, LMSPA came, I think, at a time when these concepts have been pretty well formed.
I mean, Richard Feldman has even gone through a few design iterations on the LMSPA example.
I mean, he hasn't drastically changed things, but I think with the 19 upgrade, he changed a lot of things, not just to upgrade directly to 19, but he did some design changes.
So it's been able to really mature.
And a lot of people have used that as a template for how they structure their their SPAs.
And so it's a pretty battle tested pattern.
Yeah. And so it's pretty uncontroversial and works very well.
Yeah. So the LMSPA example from Richard is also using that same pattern.
Yeah, that's right.
I mean, it's heavily influenced by the LMSPA example real world repository that Richard made.
And for anyone who doesn't know, that's just, you know, there's this spec of a real world app, which is like the medium blog platform.
It's like a clone of that tool.
So it's just a sort of non trivial application that gives you a sense of different front end languages.
So one thing that I think we should also mention in terms of just the mechanics of LMSPA is that so there are these different sort of constructor functions for different types of pages.
So as Ryan calls them, these page components.
So Elm browser has browser application browser sandbox, right, these different levels of complexity.
If you want like a browser application gives you the ability to hook into the routing of an Elm map, whereas browser sandbox doesn't.
And it just takes over a single element, whereas browser application takes over the whole page.
There are these different levels of complexity that you can use to, you know, if you're learning Elm,
then it's nice to not deal with all that complexity or if you're just wiring something into a little part of the page,
then you don't have to deal with the complexity of the routing because you don't need it.
So it's worth noting that LMSPA uses a similar pattern, except it's not browser application for the entire application, but it's for these page components.
So you have page static gives you something that just has a view and it doesn't have an update message.
Yeah, it's the equivalent of just returning an HTML in Elm.
Yes, that's exactly what it is.
Yep, it's a non dynamic page.
You have sandbox, which has an init update view.
You have element, which gets subscriptions and you have application.
Now that's the juiciest one.
Application introduces another concept that's unique to LMSPA, which is save and load.
Oh, with their toes then.
So save and load.
Yeah, there's a really interesting design and I had a good chat with Ryan.
I actually, I was picking his brain because I'm working on a design similar to LMSPA for Elm pages where it generates some code for you.
It's different because in Elm pages, it's based on this notion of a template, like a blog post template, for example.
But it's been great kind of taking inspiration from Ryan's work.
And I got to chat with him about some of his design ideas.
And one of the ones I was most curious to hear about was the design he went with for save and load.
So I think it's worth kind of mentioning what would be the alternative to save and load and what problems it's all.
Yeah, definitely.
I think the problem it solves is if you have an individual page, you know, let's say you have a user settings page,
then if there's local state for that page, you know, for example, if you're editing a username in the user settings page,
perhaps you also have some global state that maybe shows the username in the nav bar.
So how do you send that information to tell the global state to change when the username has changed, for example?
So one way you could do that is if you have the ability to trigger global messages.
And so, for example, you could do that by attaching a global message to something.
So global message would be a message designed for something higher than a page, but just for the whole system.
Exactly. Some state that's shared by all the pages, which actually is why Ryan uses the term shared, not global.
So that was a definitely nice, nice point that he made.
He was telling me that he used to call it global and it's now called shared.
And the reason is because he was explaining it to people.
He's like, oh, global is just this place where you can share state. It's like a shared state.
He's like, why don't I just call it that if that's how I'm explaining it to people, which I think makes a lot of sense.
Yeah. The way I see it is when you switch pages, you don't want to reload all data that you previously computed or fetched.
So you can store it in the shared state, shared model, and then the new page just takes it from there.
Yes, that's right. Because you need to persist certain bits of state.
For example, if a user is logged in, it wouldn't make sense to recreate that state every single time.
I mean, you're making an HTTP request to authenticate each time and what are you even using to authenticate it because you've lost.
Do you have to ask them to log in every time they go to a new page?
So obviously you need some sort of shared state.
So you could persist that state by allowing the views of different page components to send a global or shared message.
To do that, you would actually need to pass in a to message and to global message to allow you to wrap messages in either the page's local message type.
Yeah, to message would be to message, not to the number two.
That's right.
I also call it the translator pattern.
Yeah, Richard sometimes calls it the how to message pattern, I think.
Teach me how to message.
So that's one way to approach that problem.
And Ryan went a different direction and his thinking was essentially that he didn't want to couple the individual pages to the shared messages.
And he also wanted to make the way that you share state between an individual page and the shared state be very explicit.
So that's the sort of rationale behind the load and save design.
So what do they do?
Load and save are really just a hook that you define when you when you define, you know, you say the source slash pages slash settings dot elm.
And then you define your page component by saying page dot application.
And so you pass it in it update view subscriptions as we're kind of used to.
You define a message and a model type.
And then you also have these two other functions that you pass to page dot application.
And that's load and save.
And what those do is load allows you to you receive the shared state every time it changes.
And you can take that shared state and update your page component state so you can update a settings page.
Yeah. So you get you got the shared model, the model, and then you turn the model along with some commands that you can also trigger.
Yes, exactly.
Local messages.
That's right. And then you've got the save hook, which is again, it's another function that you pass to page dot application.
And that allows you to take the local model.
Given a change that you've made to your local model, it allows you to update the shared model.
And so in that way, you can communicate between the shared state and the local state.
And one of the reasons that Ryan chose this design is because it allows you to be very explicit about how you're sharing state.
So if you're looking to how do I understand how the shared state is going to change?
Well, you look in the top level update function for your shared component for your top level shared state.
And then you look in the save functions in your page components.
And between those, you can understand how the shared state could possibly change.
Yeah. So you also have a update function inside the shared module, which can also modify the shared model.
It will mostly be through the safe functions as far as I understand it.
Yeah, I mean, it could. So this actually is another good point is that shared has a view and the shared view allows you to sort of wrap the individual page view with some sort of header and footer, for example, as many apps do.
Now, if you if you wanted to, your header could trigger a shared message.
Yeah. Right. Because in shared view, that function has the ability to make messages of the shared message type, which can change the shared state.
So if you wanted to, you know, have a hamburger menu on your top level pages, if it's in mobile, you can track that open closed state of the hamburger menu in your shared model.
And you can directly send messages to toggle that state through the shared view, for example.
I do wonder whether that's a good idea. Because like, if you have a website where in all pages, you have the header, and all the pages, you have the footer, then okay, if you have some pages, like, I don't know, a settings page or admin page, where you don't have the same header or no header at all.
Mm hmm. Then you need to do things differently. And maybe you need to move all those headers and footers in the visual pages or say, oh, if you're on this page, then do not display the header display this other header and do that in shared view.
Yeah, there are different ways to structure this. So with Elm pages, the design that I'm working on is the type of your sort of shared view can be any type, which means it could be a custom type.
So the way I would solve that with the design that I'm working on the prototype I'm working on is that you could have your shared view type be a custom type that's, you know, standalone window, or a window with a header and footer.
And your individual pages can specify which type of page they're going to be if they're going to be one with a header and footer or one without.
Yeah, but and they would specify it through their shared model, they would save.
They would specify it through the well that that would be one way you could do it.
That's definitely one way you could do it with this design I'm working on and on pages, the view can be any type. So you could have a view that's a custom type that's either with header and footer or plain, and that wraps, you know, whatever view type element or HTML, and you return that type of view from the individual pages.
Well, I think this is this is an interesting thing. In Elm SPA version four, you had a CLI which generated a lot of code.
Yeah, the wiring, boilerplate, but basically everything, even the main functions, even the main files, as far as I remember.
But version five decided to only have a CLI and this is something that didn't notice when it was released.
There's no underlying Elm package. Yeah, there's no underlying Elm package.
When you create an Elm SPA project, it creates the main file, the SPA page documents, URL files, but those you can afterwards modify to your liking.
So if you wanted to, you could now specify, oh, I don't want to view to return a document with a title and a body. I want it to return a custom type.
That's right. And you couldn't do this before because that's right. It was a package and it controlled what you did.
Now it only sets the backbone of the application. Right. And you can go and modify it if you need to suit your needs.
Right. It basically gives you this SPA slash generated slash pages and slash route file.
Yeah. So the things in that that generated module namespace, that generated folder, you cannot modify or you'll be very unhappy if you do.
And the other things you can modify. Yeah. Yeah, you're right, though. You could you could approach that in those two different ways.
You could approach it by updating your shared state to say on the initial load of a page component, you could update the shared state to say whether or not there should be a header and footer.
Or you could modify the view type to be a custom type and do a case statement on that.
Yeah. I don't know about the history of why V5 was turned out to only be SLI compared to V4 where it was a CLI and a package.
I'm thinking that it's because people wanted to customize all those things, all the main file, the shared file.
And you get them, but you can modify them. I think that's true. I didn't I didn't talk to Ryan specifically about that, but I think I think.
You had three hours. I did. I did. It was a very long conversation.
I also just had a lot I wanted to ask him about as I was working through this Elm pages prototype.
It was great. It was really fun. But another thing that that it helps with is it gives you the ability to see I have a sense of these things because I've I've gone through the experience of trying to build it and like you run into a wall and then you turn the other direction.
It's interesting to compare notes. So one of the things that it gives you when you when you generate those files as well is you don't have a bajillion type variables everywhere because you can hard code it for the specific types in the code base.
So when you publish an Elm package, you can't depend on types that are specific to the user's code base.
When you generate code, you can. Yeah. I remember that V1 I think had like nine type variables for the page type or something.
Yeah. And I think it was more coupled to the idea of like specifically having an Elm UI based or Elm HTML based app, which, you know, when you when you can generalize those things, it's a sign that it's a good design, I think.
Oh, sorry. It's not nine. It's eleven in V1. Page had eleven type variables.
And I think. Oh, nice. In V4, he did some nice improvements. I didn't follow to him.
But it was some good improvements where it was only like three type variables and the rest was generated.
The earlier versions also tied in some specifics about, you know, doing page transition animations and things like that, which he, you know, he later sort of backed out of that direction and said, like, I don't want to couple the framework to those decisions too much.
I want to let the user code be more in charge of that. Yeah. And you can still do it yourself. You can still do page transitions.
Exactly. So I think the design he's come up with is very nice in its simplicity. It hits it. I think that's what makes this a really nice framework, is that it hits at the key design problem and it doesn't try to do anything more than that.
Yeah. You have simplicity and you have features. Like if I want to have page transitions, now I have to write them myself.
That's right. In my case, I don't know how to do that. So it's nice when the tool does it for you. But when you do too many things for the user, then they can't back out of it.
They can't like, oh, you're using LMSPA. Oh, you want that kind of transition. Oh, yeah. You need to stop using LMSPA.
That's right. Exactly. Because it makes certain assumptions then when it's building in these very specific features. So, yeah, Ryan's done a very nice job making the right assumptions that are safe, fairly uncontroversial assumptions to make about how you structure an SPA and assumptions that give you real benefit and value like file path based routing.
You know, like each page component is, you know, initialized in exactly the same way. And it can do all these things for you based on those assumptions. But then the more assumptions you make, the more it can limit you.
So it gives, like you said, an assumption can give you like a nice feature, but it can also limit things that you might not be able to do because of that constraint.
Another thing that I like is because everything is generated or committed to the project, you don't have a package which limits you in other ways.
And one side effect of this is that if you, for some reason, don't like LMSPA, like you created your project with it, but you just decide to stop using it, then you just stop using it. You commit the generated files.
And now you do just regular Elm. And that is really nice that you can back out of it easily.
It's a really great point. Yeah. Yeah. You're not even coupled to an Elm package. You own that code.
Yeah. You still have the whole architecture that it built for you, which is, I think, pretty good.
Yeah, it's actually pretty readable code. Yeah. Perhaps even more readable than your average SPA code base because a lot of thought has been put into how to design it and structure it.
Yeah. And it's very consistent. Yeah. It's mostly what you would do yourself, except that it does something with view and subscriptions, which it bundles together, which I don't do in my normal code.
But it's not very hard to understand, I think.
And by the way, I don't think we've mentioned yet that the CLI provides this command you can run, which is LMSPA add.
Yeah. If you do that, it will walk you through interactively choosing which type of page component do you want to use.
Do you want to have a static sandbox element or application type of page component?
Then it will ask you for a name and then it generates that starting point.
So that's quite handy because it takes care of that wiring for you.
One thing that's kind of interesting to note is I think it does depend on you exposing certain things. Right.
But when you use the CLI to generate the files, you don't have to think about that so much.
Yeah, because it will give you errors if you messed up somehow.
And it generates it for you. So you don't have to do it by hand.
If you were doing it by hand, you know, and it's the kind of thing like you have to remember to expose message and model and that they're named that same thing.
But usually people will do that. But when you generate it with the CLI using LMSPA add, you don't even have to think about that.
Yeah, actually even forgot about the feature. And when I played with it, I copy pasted another page.
Yeah, that's that's a fair way to do it, too.
Which in my case, I really just wanted to copy paste.
But for some reason. So what are the problems with the load save paradigm or idea?
Because there is a trap in the sense that you can forget to load things and you can forget to save things.
Exactly. I think those are the only downsides that I really see with it.
Yeah. As with any LMSPA code, things work so nicely when you have a single source of truth for data.
And when you start duplicating that source of truth, then things can get out of sync.
But in general, if you approach writing LMSPA code in this way where there's a single source of truth, then you just don't even think about out of sync errors because they don't happen.
It's interesting with like an object oriented language where, you know, you have mutable objects.
You can just update an object and the data stays in sync.
But with immutable languages, you can have something that's the same object, but it diverges in state because you're you're passing around these immutable data types, which can then have new references to different values.
And so they can get out of sync. So you do you do run into that issue.
And that's something you have to be aware of. Yeah.
And it's only something that you will notice during page transitions and maybe only some page transitions, like when you leave a given page, which forgets to save things or when you enter a specific page, which loads some other data.
That's right. And on the flip side of that, you can intentionally choose to update things selectively.
Yeah. So you can choose. I want to forget whatever was before. Like when you go to the logout page, you can say, oh, user equals nothing.
That's right. Yeah. Yeah. I do think that Elm Review could help with this, like not forgetting to.
Yeah. To load and save things that are named the same as in the shared model, for instance.
But I do think that there's a lot of edge cases where you might not want to. You might have intended.
Yeah. So keep them out of sync. Yeah. I'd be interested in a thing that's working. But yeah, it's it's an idea.
That's an interesting point. So so in terms of the you know, the value from from using Elm S.P.A., you know, let's let's say, you know, using it in a production code base,
using it for a team. We kind of hit on a few points. We talked about the value of having a one to one mapping of a of a route to a particular place in the file system.
Yeah. We didn't say that pages are much simpler because you don't have to handle all the wiring.
That's right. It takes care of a lot of wiring for you. And it does it in a very consistent way because it's like if you don't, it won't get wired in for you or there will be an error.
You just use Elm S.P.A. add and it, you know, it uses a particular structure and it and it auto generates these pieces for you that are going to be done very consistently.
Yeah. Basically, unless you're using a page dot application, you can see every page as a single program.
Exactly. And even for application is much simpler than the wiring that you would do yourself, I think.
Right. Exactly. So that's huge. Just just the ease of navigating through a code base and being able to predict how things are going to be wired up, where you're going to find things.
That's extremely valuable, I think. Another benefit of using Elm S.P.A., which I think is really valuable to to consider as you're as you're using it,
is how it enables you to think about just a slice of, you know, think about one page at a time, but also think about, you know, if it's if it's built with page static, then you don't have to think about the global state or the local state.
You know, if it's page sandbox, then you don't have to think about subscription.
So, you know, you get these more nuanced ways of looking at a page and you know, is this going to touch shared state? And you can simply look through for is it a page dot application?
If it's not, you know, it doesn't touch the shared state. So this is one of the amazing things about Elm is your ability to look at type signatures and know what it could possibly do.
If it doesn't get wired up to an update function that sends a command, then it doesn't do side effects and you don't have to think about that.
If it doesn't get wired up to update, it doesn't change the state. If it doesn't depend on this data, then it's not using it. If it doesn't return this data, then it's not affecting it.
I think we could link to Richard Feldman's scaling Elm apps.
Yes, exactly. Yeah, Richard Feldman gave an Elm Europe keynote one year and it's yes, it is a treasure trove of great advice on how to approach building Elm apps.
And basically the headline of that talk, the advice he gives is make your Elm code as simple as possible and no simpler in terms of what it depends on and what it can change.
So that, you know, if you're structuring your Elm code to make it depend on as few things as it needs to and be coupled to things that it can possibly do and change as little as it possibly can,
it makes it so much easier to trace where problems are coming from because you can completely discard certain things as possibilities by just glancing at the type signature.
And that's a superpower as an Elm developer to be able to read things that way and to structure things so it's easier to do that, which definitely watch Richard's scaling Elm apps talk to learn more about that.
In the context of Elm SPA, it gives you that same thing as a framework.
You have these sort of four core ways of building an app, page.static, page.sandbox, page.element, page.application, and that gives you a very clear understanding of what it can possibly do in terms of affecting global state.
Does it have its own state? I think that's very valuable.
And since those are generated for you when you create the project, you can modify those. So if you want an application but that can't update the shared model, then you create a new variant of application where you don't have the save function, for instance.
And when you use that, you know, oh, okay, that one can't update the shared model.
Okay, so it is generated for you, but it's not in the generated folder. And so you could change it, but it's also going to call.
Oh, I guess you would have to change your your upgrade functions. But the upgrade functions are generated. Are you sure you can change those?
Are you sure it doesn't make assumptions based on that?
No, you just created a new variant of application where the wiring is different. Like you say, save is always identity, meaning it never changes the shared model.
I gotcha.
And those files are committed to the project.
Gotcha. So there's like this page type. I guess you couldn't change the page type.
There's this like type alias page, which defines all of the, you know, init, update, view, subscription, save, load.
I guess the generated code depends on assumptions about that, right?
I'm guessing it would be unhappy if you changed that. I'm guessing it's assuming that the type of page is that.
I wonder about that, actually.
Yeah, I guess it wants to have an init or an update function and view or bundle.
Yeah, exactly. Because the generated code is passing specific types of data types into page.init.
And so I think things would break if you changed that type alias, which actually maybe that's something that could be made more clear by that being in like the generated folder.
Well, I do think it's useful not to have it be generated.
But as long as you keep having pages generate this, the current page alias, then it's fine.
Right. Yeah. He may have kept the page type alias out of the generated folder to avoid some circular dependencies.
But also, yeah.
It's nice when you can edit anything outside of the generated folder as long as you keep things consistent between your own code and don't get any compiler errors.
And then just have the generated stuff depend on only generated stuff.
Yeah. And the downside is if you change those types too much is that if someone is used to MSPA but not to your project and they come into your project,
then they will have a bit of a harder time to understand things. But generally, it will stay probably the same thing.
Right. I think. Yeah. I think there's a lot of value to having the ability to customize these types of things for an application. Yeah.
But also to keep things pretty standard.
Yes. Yeah. Two sides of the...
Mm hmm. Right.
So, yeah, there's one thing that I want to pick your brain on is about boilerplate.
So in Elm, we don't like boilerplate, but we embrace it more than we do than other communities do.
Yeah. And in this case, the boilerplate is handled for you. And I'm thinking that in this case, it's fine.
I usually don't like it when there's magic involved because you can't see how things are working. And when you're a beginner, it's hard to understand.
But in this case, I think it's fine because the code is generated and you can trace it back and you can understand the whole system.
Yeah. Is that your feeling too or?
Yeah. One way that I think about it.
So first of all, I think that the reason that in the Elm community, we tend to hear people talking about boilerplate and we say,
hmm, are they thinking about the right considerations for designing something?
Is because what the tradeoff often is, is you can have boilerplate or you can have implicit magic code.
Yeah. And we don't like that in Elm. So we don't like boilerplate in the Elm community.
But the sensibility is we like implicit magic much less than we like boilerplate.
And so we'll take that tradeoff. And that's very frequently the experience I have when I use things in the JavaScript ecosystem or in the Ruby Rails community, for example.
There's a lot of code that does things for you, which is convenient.
But then what I find when I'm working with code like that is that means that's more for my brain to process.
So it's like this framework does this work for me, but my brain still has to understand it.
So it's actually adding more cognitive load. That's the way I think about it.
So how can we make our code more predictable and make it cheaper to process what's going on and make it easier to trace back bugs and understand code and understand how to change things?
So that's the goal. And being explicit helps with that goal. There's less work for our brain to do.
Even if there's more work for our fingers to do to initially type it, that's OK.
We want to optimize for our brains doing less work and our brains being able to better predict how things will change or where problems are coming from.
So that said, when we were talking about the boilerplate of an SPA in Elm, you can create a very simple set of rules that the code is generating for you.
And now if you think about that question, how much effort does this add for my brain to understand and how well can my brain predict what changes are going to do?
It actually isn't a problem for those considerations because there's a very predictable deterministic mechanism for how the stuff you write is going to map to this lower level code.
So what you're actually doing is you're learning a higher level mechanism and mapping that understanding to the lower level pieces of what that's going to do.
So your brain can still understand it just as quickly, if not quicker, because you know it's not going to deviate in all the low level wiring.
You know it's going to be predictably wired. You know exactly how to map it to what it's going to do and how the code is going to change when you change something in a particular spot.
So I think it actually, yes, there are a couple of concepts that you have to learn.
But once you learn those concepts, I think those concepts serve to help you understand code more quickly with less effort.
So in my book, that is definitely a win. And I think this framework does a very good job of balancing those trade offs.
Well, I think anyone would say as long as you learn the concepts, it makes everything much easier.
And yes, be that for language, be that for a framework.
And yeah, it's true. But sometimes the learning curve is very hard.
And in this case, it's pretty simple. So it's OK.
But otherwise, be careful of having to teach people new ways of thinking that you can't trace back, that you can't understand without looking at the source code or something like that.
Exactly. And I think frameworks have to, you know, framework authors have to seriously consider how many concepts they're introducing to users because it becomes too many concepts to juggle.
So if you have too many concepts that you're introducing, then users can't have a clear mental model.
There's a steep learning curve. And even if they do learn it, they can't keep it straight in their head because there are too many concepts and it's confusing.
So I think another important point is that the concepts and the mapping of the code you write to the generated code, it's actually fairly simple.
Now, you know, earlier versions, when you have page transitions and things like that, that's more specific concepts. That's more specific understanding that you have to gain in.
What is this code doing for me? And so I think that, you know, this has gotten, you know, the design has gotten better and better in that regard of just doing very little, but doing things that have a huge win.
So learning this new concept, you get your money's worth.
Back to creating new functions in the SPA page module, like static application sandbox and application.
I said you can change them, but I just noticed if you do MSPA add and you change them, you will get incorrect code to solve, which can be fine, but you will not have as much help as you would have expected.
Yeah, that's interesting. I wonder how much changing those helpers is in line with Ryan's vision and intention for the framework.
Probably not much.
We'll see. Maybe. I mean, I think it's the kind of thing too that as people use it more, this is the type of thing that could potentially evolve.
You know, maybe the CLI, you know, will introspect this file and give you those options based on that. Who knows? But yeah.
Oh, I want to try that out now.
Yeah, I want to hear more of Ryan's thoughts on that. I'll tweet about it and report back.
So if people want to get started, how should they learn about it?
Okay, so Ryan made a very helpful website, which is his documentation since he doesn't have an Elm package anymore.
So there's no entry on the packages, the Elm package registry website, at least not for this version.
So you can go to elm dash SBA dot dev and you can follow his guide. So you got something that looks kind of like the official Elm guide remotely.
We kind of learn some concepts of Elm or architecture.
I still do recommend the official guide first because it doesn't teach you Elm.
Yeah, but it's a very good tutorial. He introduces the concepts very nicely.
So yeah, go there and you will have a getting started section, which is basically install Elm SBA via NPM and do Elm dash SBA in it.
Yep, right. If you have questions, there is a Elm dash SBA dash users on the Slack channel.
You have a channel on Slack. Yeah.
Yeah, people seem to be responsive there and there's a lot of conversation happening in that channel.
Yeah. Definitely a good place to look.
And then there's Ryan recently released Elm SBA real world or as he as he considered naming it Elm SBA example example.
It's a rewrite of the real world app, which we mentioned at the top.
It's Richard Feldman did a great demo repository that builds this sort of medium blog platform clone in Elm as an SBA to show off how sort of medium scale app looks.
So Ryan built that using the Elm SBA framework. That's definitely worth a look.
If you haven't looked at Richard's original Elm SBA example, that's also worth a look just for some of the patterns he uses there.
And actually, Richard walks through that.
So he has an Oslo Elm Day keynote where he walked through some details about that Elm SBA example repo and his front end masters courses talk about it heavily as well.
So those are also great resources.
Even if you're using Ryan's Elm SBA framework, you'll be served well by by learning more about SBAs and Elm in general.
Yeah, I would really like a section in the guide to how to migrate your projects to using Elm SBA.
Yeah. The advice I would give is see the code that is being generated by Elm SBA or for new packages and try to make your your pages fit that model one by one.
And then when you have something very much like SBA, switch to using Elm SBA is my guess advice.
Yeah, I'm trying to think if there would be because you can't really create two different Elm browser applications side by side.
And Elm SBA uses browser application.
So I'd be curious to hear how people have been approaching that.
I know some people have used some large scale projects and migrated over to Elm SBA and they show their, you know, diff for the pull request.
And it's, you know, thousands of lines deleted compared to the lines added.
And it's quite cool. But I'm curious, do they just go for it and start wiring it in?
Or do they do it sort of stepwise where they they have sort of the Elm SBA version and their original versions side by side and both working in production?
That's always my leaning is to try to do that if possible.
But maybe people are just going for it and doing the wiring because they're confident that it'll work.
I don't know. Yeah. Well, I guess if you have a small application, then, yeah, sure.
But it really depends on the size. All right.
Well, are you going to are you going to use it, Jeroen?
I don't know if you want me to say yes or if you want me to say no, I'm going to use Elm Pages.
Oh, well, that's that's an option, too. But I mean, they serve very different purposes, I think.
But no, I'm curious. Are you are you going to be using it for any apps that you've got?
Well, at my workplace, we are gradually migrating to something like Elm SBA.
We were actually pretty inspired by the SBA before. Yes. Oh, OK.
So we were going into that direction and then V5 came out.
So I'm still a bit curious about how the load and save will turn out in production.
Yeah. Yeah. Which is the big change in V5, in my opinion.
But yeah, I find it very interesting. So, yeah, I guess we'll try it out.
And then I don't really have any of my own projects, which have their own websites.
But yeah, if I ever do want to probably do something like Elm Pages or Elm SBA.
Yeah, definitely. Or depending on size, just my using my own bullet platey code.
Right. I definitely think that code generation is one of these things that, you know,
you do have to use it somewhat sparingly and be careful how you do it.
Maybe that's a topic for a whole episode, but you want to make a very clear mapping between the source of truth of the code.
Ideally, it's some Elm code and what it's going to do with that as the input.
But code generation is certainly a very powerful technique in Elm when it's used sparingly and in the strategic places.
And I definitely think that Elm SBA is one of those examples. So, well, great stuff.
Yeah, we did talk about code generation in the episode called Extending Elm, episode seven.
We don't often go back to our previous episodes, but hey, go listen to episode seven.
That's right. Great stuff. Also, if people haven't done so yet, we would love to get ratings in the iTunes podcast app.
So if you go to the Elm Radio podcast in iTunes or in Apple podcasts, all you have to do is go to the podcast and tap in a star rating.
And that helps people find the podcast.
The rating the most on the right.
Oh, that's right. Yeah, yeah. Just press the fifth star and you'll be good.
Also, tweet us your questions or go to slash question.
Yeah, we also have a Twitter account now which is called Elm Radio Podcast.
Yeah, let us know what you want to hear about and let us know if you're using Elm SBA. We'd love to hear about it.
Yeah. All right. Well, thanks again, Jeroen. And I'll talk to you next time.
See you next time.