spotifyovercastrssapple-podcasts

elm-app-url with Simon Lydell

Simon Lydell joins us to talk about a new approach to URL parsing in Elm that is both simple and powerful.
February 27, 2023
#77

Transcript

[00:00:00]
Hello, Jeroen.
[00:00:01]
Hello, Dillon.
[00:00:02]
Well, I thought about talking about the subject just the two of us, but I thought it would
[00:00:08]
be a lot better if we brought in a domain expert.
[00:00:11]
What do you think?
[00:00:12]
I think that sounds great.
[00:00:15]
And that domain expert is Simon Lydell.
[00:00:19]
Simon, thank you so much for coming back on the podcast.
[00:00:22]
Hello.
[00:00:23]
Glad to be here and great to be back.
[00:00:26]
Yeah, you are our domain expert.
[00:00:29]
So are you trying to force a pun?
[00:00:33]
Never.
[00:00:34]
Me?
[00:00:35]
No, you.
[00:00:36]
Yes.
[00:00:37]
Simon, what are we talking about today?
[00:00:42]
Today we're talking about elmappurl, which is a new package that I made, which is related
[00:00:50]
to a domain, but it's also exactly what it isn't.
[00:00:54]
Right.
[00:00:55]
Yes.
[00:00:56]
So it was a bad pun.
[00:00:59]
Yeah.
[00:01:00]
So what exactly does it do?
[00:01:06]
elmappurl is an alternative slash complement to the standard elm slash URL package, and
[00:01:15]
it attempts to be simpler and easier to use.
[00:01:19]
Right.
[00:01:20]
Yeah.
[00:01:21]
So you talked about, you've got a really nice short intro video that I definitely recommend
[00:01:25]
people take a look at.
[00:01:26]
We talked about this blog post that sort of went into all of the types behind the elm
[00:01:34]
URL parsing, and you completely bypassed that with your design here.
[00:01:39]
Yeah.
[00:01:40]
So the way I'd like to see it is there are like three main things that they're dissatisfied
[00:01:47]
with the official elm slash URL package.
[00:01:51]
And they are more or less a design flaw and escape incident and the Jurassic Park anti-pattern.
[00:02:00]
I'm intrigued.
[00:02:01]
Okay.
[00:02:02]
Me too.
[00:02:03]
And we probably need to keep that one for last since you mentioned it last.
[00:02:09]
So let's get started with the rest.
[00:02:13]
I actually think it makes sense to start with a Jurassic Park.
[00:02:17]
Oh, great.
[00:02:19]
Awesome.
[00:02:21]
It's the most interesting part and it ties back to what Dillon said with the intro in
[00:02:28]
the readme.
[00:02:29]
Is it that our scientists stopped to ask if they could, but not whether they should?
[00:02:34]
Yeah, exactly.
[00:02:35]
That's the whole thing.
[00:02:38]
Awesome.
[00:02:39]
Okay.
[00:02:40]
So in my opinion, URL.parser in the standard elm URL package, in hindsight, maybe just
[00:02:50]
should have been a blog post and not a package.
[00:02:54]
And what I mean by that is like, I read a tweet, like, I don't know, a year ago, where
[00:03:00]
someone said, like, did you know that all URLs are also valid JavaScript code?
[00:03:07]
And then they explained like, if you start with the HTTP colon, that's a labeled statement
[00:03:14]
in JavaScript.
[00:03:15]
Oh, no.
[00:03:16]
And then you have slash slash, and that's a comment.
[00:03:18]
And the rest of it is just the rest of the comments.
[00:03:23]
So like, that's a fun thing.
[00:03:25]
That's something I could see myself waking up thinking like, I never thought about that
[00:03:30]
before.
[00:03:31]
I'm going to try if that works.
[00:03:32]
And maybe it does.
[00:03:33]
And I learned something.
[00:03:34]
And I like to imagine, this is of course not true.
[00:03:38]
This is just my imagination.
[00:03:40]
What if Evan woke up one day and was like, could I like take a URL and have it be valid
[00:03:48]
code?
[00:03:49]
And if you think about it, if you have like a URL, let's say HTTP colon slash slash example.com
[00:03:57]
slash A slash B, and you only take like the last part slash A slash B, you get rid of
[00:04:02]
the first slash, then you only have A slash B. And like, that looks like division.
[00:04:08]
So you could probably make that work as code, define A and define B, set them to floats.
[00:04:14]
And when you do the division, you get another number as the result, right?
[00:04:19]
And then you could like take it one step further in Python, you could do operator overloading.
[00:04:26]
So if you make a class in Python and define a certain method, then you can like decide
[00:04:31]
what it means to do division with two such classes, for example.
[00:04:35]
And then you could like decide that, okay, division means that I'm going to return not
[00:04:40]
a number, but maybe a function.
[00:04:43]
And that function could do something interesting, like parse a URL that looks like A slash B
[00:04:48]
and tell you like, it was A slash B or it wasn't.
[00:04:53]
But then in Elm, we don't have operator overloading.
[00:04:56]
But before Elm 0.19, we had custom operators.
[00:05:02]
So you can define your own, which is exactly what Elm slash URL package does.
[00:05:08]
And slash was occupied for division, it like added less than and greater than around the
[00:05:15]
slash sign.
[00:05:16]
And now you have an operator that kind of looks like a slash.
[00:05:19]
And then you could like create a parser type and define some small functions that you could
[00:05:25]
use with this slash.
[00:05:26]
And then you can like kind of take a URL, paste it in your Elm code, tweak it a little
[00:05:32]
bit and then you have like a working parser for that URL.
[00:05:36]
And that is a really cool idea in my opinion.
[00:05:39]
But now we have talked about like, let's think about if we could do it.
[00:05:45]
But should we do it?
[00:05:47]
Right.
[00:05:48]
And in my opinion, like you could have written a blog post, how you came up with this, how
[00:05:54]
you implemented it, what you learned, and then also pros and cons of the approach.
[00:06:00]
And the pros is maybe that perhaps you could take a URL and turn it into code quite easily.
[00:06:07]
But the cons in my opinion is that there is quite a lot of type trickery involved to make
[00:06:13]
this work.
[00:06:14]
That is fairly non obvious to work with, at least as a beginner, I think.
[00:06:20]
And also for me.
[00:06:22]
Yeah, so you're mostly talking about parsing a URL, right?
[00:06:27]
So you have a string and you give it a specification, three parser type, and then you run it and
[00:06:35]
the types that are involved in that are very complex.
[00:06:38]
Yeah, there is a parser type in the package.
[00:06:43]
And it takes a, or it makes sense if it takes a type variable, because if you're parsing
[00:06:49]
something, you want to decide like what type should it parse to, what type have I chosen
[00:06:55]
to represent all my routes in an app with.
[00:06:59]
But if you look at the actual parser type, it doesn't take just one type variable, it
[00:07:05]
takes two of them.
[00:07:07]
And every time I try to write a type annotation for a URL parser, I can start out quite easily.
[00:07:14]
I type like URL dot parser space, and then I'm stuck.
[00:07:19]
And I can't figure it out.
[00:07:21]
So I just erase all of that, and then I use my editor action instead, infer type, and
[00:07:27]
it puts it in for me.
[00:07:29]
And the first type variable is a function that like takes my type and returns an A.
[00:07:36]
And the second one is also an A. And I have no clue what the function is doing there and
[00:07:41]
what the A is.
[00:07:43]
Yeah.
[00:07:44]
So having been through this process yourself, can you, in simple language, explain what
[00:07:52]
that extra type variable actually means?
[00:07:55]
Because I still find it extremely confusing.
[00:07:58]
No.
[00:07:59]
I read the blog post about it, but I just don't remember.
[00:08:05]
It's just gone.
[00:08:07]
We will then link to the blog post and we can all get out of having to put that into
[00:08:12]
simple words.
[00:08:14]
Is it kind of like monads?
[00:08:16]
Whenever you read about them and you understand, then you can't explain it again.
[00:08:22]
In this case, you read it, you don't understand it, and you still can't explain it.
[00:08:26]
And then you design a whole package so that you don't have to deal with that anymore.
[00:08:31]
And then you're invited on a podcast and asked to explain it.
[00:08:35]
And then your name is Simon.
[00:08:37]
Whoa, that's very specific.
[00:08:44]
Classic scenario.
[00:08:47]
So you designed a Jurassic Park version that's the well there it is version of the Jurassic
[00:08:54]
Park takeaway.
[00:08:55]
And so what were the...
[00:08:58]
You talked about the pros and cons.
[00:09:00]
So the cons are you have this challenging type to deal with.
[00:09:05]
The pro is it kind of looks like a URL, but it's not necessarily...
[00:09:11]
See that's an interesting question is, does that make it easier to understand what it's
[00:09:15]
doing because it looks like a URL?
[00:09:18]
And I would say, if you step back and look at Elm's history a little bit, in the earlier
[00:09:24]
days, there was, I think there were more roots in kind of Haskell design and a little bit
[00:09:33]
like in the ecosystem of Ruby design.
[00:09:36]
Like Richard Feldman's Elm CSS early on was trying to use a syntax where you design these
[00:09:45]
style sheets that looked like CSS syntax.
[00:09:48]
So you could have a custom operator for hashes and a custom operator for dots.
[00:09:53]
And there were like a few things that didn't quite work out neatly.
[00:09:57]
So you had to use some special trick for that.
[00:10:01]
And then eventually he just got rid of all of them and he said, this actually, like,
[00:10:04]
yes, it makes it kind of look like CSS syntax, which is kind of cool.
[00:10:09]
And in the Ruby community, people really liked making DSLs that looked like the thing they're
[00:10:14]
supposed to be.
[00:10:16]
But then he moved to a more straightforward design that focused on making it really easy
[00:10:22]
to understand how the pieces fit together and making it more maintainable.
[00:10:26]
And I think like a lot of the Elm design instincts have moved in that direction too.
[00:10:33]
And to me, your design for Elm app URL feels like that as well.
[00:10:38]
So should we explain how parsing was done before?
[00:10:42]
Like how did the code look and how it's done in your solution?
[00:10:47]
Sure.
[00:10:48]
So we need some kind of example URL to talk about.
[00:10:52]
And maybe we can do like slash product slash, and then like a product ID or a product slug.
[00:11:01]
We can go with a product slug.
[00:11:04]
So we have a hard coded part, which is always product and then slash and then a dynamic
[00:11:09]
part.
[00:11:10]
And then the official package, you would, you would use this function called just S.
[00:11:18]
I think it's for segment.
[00:11:19]
Oh, I thought it was for string, segment makes sense.
[00:11:23]
I think so.
[00:11:24]
At least I think the point is that it's supposed to be short.
[00:11:27]
Yeah.
[00:11:28]
So the code looks more like a URL and less like code.
[00:11:33]
And the segment function, it takes a string and there we would give it a product as a
[00:11:39]
string.
[00:11:40]
And that's how you say that I want a hard coded piece, which is product.
[00:11:45]
And then you use this custom slash looking operator to join that with the next piece
[00:11:51]
of the URL, which will be the dynamic part.
[00:11:54]
And then there's a function called string, which means that this segment can be any string.
[00:11:59]
And then finally, you usually pipe all of that to URL or parser dot map to turn that
[00:12:06]
into like a custom type variant.
[00:12:09]
I guess the most common thing.
[00:12:12]
But what I tried to do was to like, I wanted elm app URL to be more of an anti package.
[00:12:20]
I want to be a small and as little of a package as possible, just to be a stark contrast to
[00:12:28]
the official package.
[00:12:30]
I was thinking like, do we need a parser at all?
[00:12:33]
Can we can we just do it some simple way?
[00:12:36]
And what is the like the simplest way I could think of?
[00:12:39]
Well, we could take the path of a URL and just split by slash and then do pattern matching
[00:12:45]
on it with a case of expression.
[00:12:47]
So try that without a package.
[00:12:49]
I just like made a little example and wanted to see what I can do with it.
[00:12:55]
And the first thing I noticed was that it's kind of annoying to split by slash yourself,
[00:13:02]
because the path always starts with a slash, which means that you get a list with an empty
[00:13:08]
string.
[00:13:09]
And then the first thing you're actually interested in, which would be the string product in this
[00:13:13]
case, and then all of the rest of the things.
[00:13:16]
And also, I noticed that the parsers in the official package, they also ignore a trailing
[00:13:23]
slash for you, which is kind of common that sometimes you end up with a trailing slash
[00:13:28]
and maybe your application uses it, maybe it doesn't.
[00:13:32]
And then you have to handle that as well in the pattern match, like add an extra case
[00:13:36]
for an empty string at the end.
[00:13:38]
So that was kind of annoying.
[00:13:40]
But then I thought, like, the official package has a URL type, which is just a record with
[00:13:46]
all the pieces of a URL and one of them is called path.
[00:13:50]
And that is like the slash product slash product slug piece.
[00:13:55]
I was thinking like, why is that just a string?
[00:13:58]
Couldn't you have parsed that for me into a list of segments already?
[00:14:03]
That would have been so much easier.
[00:14:05]
So I thought like, that would be the first change I would make to this package.
[00:14:11]
So then I like create a little helper function in my example that did that.
[00:14:15]
And now my parsing function got really nice because it's just all I'm doing in the function
[00:14:22]
is case URL dot path of, and then I match different string, different list patterns.
[00:14:32]
So to take our example, the pattern we're looking for is a list with just two elements.
[00:14:39]
The first one is the string product.
[00:14:42]
The second one is any string.
[00:14:45]
So you can just type the name of a variable there.
[00:14:49]
And in inside the pattern, all you need to do is return just whatever you want.
[00:14:56]
And that's it.
[00:14:57]
That's all there is to it.
[00:14:59]
That is much simpler.
[00:15:01]
Yeah.
[00:15:02]
So you need to have a default case for any other routes that you're trying to parse.
[00:15:09]
And I'm guessing that would return a 404 custom variant, usually?
[00:15:12]
Yeah, I've seen different ways of doing it.
[00:15:17]
So that's one way of doing it.
[00:15:19]
If you have a custom type with all your routes, you can just one more alternative, which is
[00:15:24]
not found or 404.
[00:15:26]
You can also choose to return a maybe in this function.
[00:15:30]
So either just some route that exists or nothing, which means that nothing matched and you can
[00:15:37]
handle that one level above and turn it into a 404 page.
[00:15:42]
Yeah, so this package is really a pattern as much as anything.
[00:15:48]
And the code is just to support that pattern.
[00:15:50]
And so in a nutshell, that pattern is like you've got a nice example showing your product
[00:15:56]
example here.
[00:15:58]
It's just a function that takes an app URL, and it returns a maybe route.
[00:16:03]
Or if you wanted to have a 404 route, be a variant, it could just be a route.
[00:16:09]
But that's all it is.
[00:16:11]
Maybe we should just explain what an app URL is.
[00:16:14]
You said the function took the app URL.
[00:16:17]
Yes.
[00:16:18]
So app URL is the central type of my package.
[00:16:22]
The difference to the URL type in the official package is that an app URL is a subset of
[00:16:29]
a whole URL.
[00:16:30]
It is only the path, the query parameters and the fragment.
[00:16:35]
So it is not the scheme like HTTP, and it is not the domain, not the port.
[00:16:42]
And the reason for that is that that is the part that you are actually interested in when
[00:16:47]
writing an Elm application.
[00:16:49]
Your app is going to be hosted on some domain, but you don't really care about that in your
[00:16:53]
Elm code.
[00:16:54]
It doesn't matter.
[00:16:55]
So it's kind of annoying to have to...
[00:16:57]
Like if you ever want to create an app URL because a function needs one, you don't want
[00:17:02]
to specify a fake scheme, a fake domain and so on, just to be able to satisfy the type.
[00:17:09]
And I read through specifications for URLs, and there is no name just for path plus query
[00:17:16]
plus fragment.
[00:17:18]
So I had to invent something.
[00:17:20]
And eventually I came up with app URL.
[00:17:23]
It's kind of short, and it also shows that it's like designed to be used for an Elm app.
[00:17:30]
Yeah, that's my thinking.
[00:17:32]
And this type or this package is only designed for parsing and stringifying routes in your
[00:17:40]
application, right?
[00:17:41]
It's not designed to represent any URL like the Elm URL package is supposed to.
[00:17:46]
Exactly.
[00:17:48]
So the way you use this, walk us through how you would wire this in compared to how you
[00:17:53]
would wire in an Elm URL parser.
[00:17:56]
So in an Elm application where you use browser.application, there are a couple of places where you get
[00:18:05]
a URL in the init function, and also in URL changed, like the URL change message that
[00:18:13]
you get.
[00:18:14]
And what you do there is instead of giving that URL to a URL parser, you run a function
[00:18:22]
called app URL dot from URL, which turns a full URL into only the app URL parts.
[00:18:29]
And then you give that to your function that does the pattern match on the path.
[00:18:35]
And then you have basically replaced the parser based stuff you had from before.
[00:18:40]
Right.
[00:18:41]
So the app URL type, so it gives you these values that are easier to deal with in a pattern
[00:18:47]
match.
[00:18:48]
So you have path is list of string, query parameters is a dict of string to list of
[00:18:54]
string, and then the fragment is a maybe string.
[00:18:57]
I'm guessing the fragment is probably not used that often in this context, but query
[00:19:03]
parameters are used commonly.
[00:19:06]
So how do you use the query parameters to in Elm app URL to parse into part of your
[00:19:12]
route?
[00:19:13]
That ties back to one of those three issues that I had with the official package.
[00:19:20]
And this is the design flaw one, in my opinion.
[00:19:24]
So the official URL type, it has a field called query, I think, and that is just a string,
[00:19:31]
which means that the query parameters, the stuff that is from the question mark to the
[00:19:38]
end or to the fragment, if you have one, it's just a string.
[00:19:42]
I've tried to think of like, why is that?
[00:19:45]
There is a link in the Elm URL package to an RFC, you know, one of those like monospaced
[00:19:55]
formatted specification lucky thingies, which is like a specification for a URL.
[00:20:02]
And in that one, for some reason, they just say that the query is the part from the question
[00:20:08]
mark to the fragment.
[00:20:10]
And usually the query is the part from the question mark.
[00:20:16]
Yeah, kind of.
[00:20:17]
But for some reason, they just say like, yeah, and people typically put key value pairs in
[00:20:22]
there.
[00:20:23]
Right.
[00:20:24]
Yeah.
[00:20:25]
So I'm wondering if like, Evan decided that, okay, I'm going to follow this RFC.
[00:20:31]
And if someone complains, like, why is the query string?
[00:20:34]
Well, it's in the RFC.
[00:20:36]
I followed it.
[00:20:37]
Right.
[00:20:38]
But in practice, like everyone uses the query string the same way.
[00:20:42]
It's key value pairs separated by ampersands.
[00:20:46]
Right.
[00:20:47]
With equals separating the keys and values.
[00:20:50]
Yeah.
[00:20:51]
Yeah.
[00:20:52]
So that is not in the spec.
[00:20:53]
As far as I can understand, specifications for URLs are pretty weird.
[00:20:58]
I read a tweet from Daniel Stenberg, the creator of Curl.
[00:21:05]
He tweeted something, someone responded and they responded with a question like, something,
[00:21:13]
something URL specification.
[00:21:15]
And Daniel's answer was just like, which specification question?
[00:21:22]
Yeah, that's a bit too highly.
[00:21:27]
These days, there is one from the WG committee.
[00:21:33]
They're the ones making the HTML specification these days.
[00:21:38]
They have like a URL specification that is a bit focused to JavaScript, because JavaScript
[00:21:45]
has a nice URL class these days with the parsed query parameters in it and stuff like that.
[00:21:52]
Right.
[00:21:53]
Isn't there like a URL params constructor or something like that?
[00:21:57]
Yeah, I think it's URL search params or something.
[00:22:00]
URL search params, right.
[00:22:01]
Yeah.
[00:22:02]
So I had the luxury of being able to piggyback from that.
[00:22:06]
Like whenever I thought like, hmm, how should this be handled in query parameters?
[00:22:11]
I could look that up and see, like, how did they do it?
[00:22:16]
Does it make sense?
[00:22:17]
Should I copy that?
[00:22:18]
And the answer was always yes.
[00:22:22]
So I decided that in an app URL query, it's not just a string, it's a dictionary.
[00:22:30]
So the whole thing is parsed for you.
[00:22:33]
Right.
[00:22:34]
So the one piece of information that might have meaning in a somewhat normal use of URL
[00:22:42]
query params would be the order, which you have the order, if you use the same query
[00:22:50]
param name multiple times, you have the order that those come in, but you don't have the
[00:22:54]
order of the relative keys.
[00:22:56]
But I mean, usually people just use them as key value pairs.
[00:22:59]
So I guess that would be kind of non standard.
[00:23:01]
Maybe we should clarify, so I chose a dict to store the parameters.
[00:23:07]
And when you use a dict, you get the side effect that all of the things in the dict
[00:23:13]
are always sorted by key alphabetically.
[00:23:16]
So if you really cared about the order of your params, then that's not really possible
[00:23:22]
with my package.
[00:23:23]
But I don't think that's a real use case.
[00:23:25]
Yeah, the only use case that I can see where this could be a problem is if you're trying
[00:23:30]
to compare the URL you had at the beginning with the stringified version of your app URL,
[00:23:36]
then if the order of keys are different, then you're gonna say, Oh, well, these are different.
[00:23:41]
Let's refresh the page or something.
[00:23:44]
I don't know.
[00:23:45]
And in that case, you have a problem.
[00:23:46]
But other than that, yeah.
[00:23:48]
Yeah.
[00:23:49]
But if you wanted to do something like having only ampersand, having five ampersands in
[00:23:54]
a row in a query parameter, that's a valid URL.
[00:23:59]
According to the URL spec, but not meaningful for your package.
[00:24:05]
Exactly.
[00:24:08]
I tried to like think of every edge case.
[00:24:12]
It's like if you have a lot of ampersands in a row, what does that mean?
[00:24:18]
And there are two possible interpretations.
[00:24:22]
One is that you just ignore all of the extra ampersands, like they like don't contribute
[00:24:28]
to anything.
[00:24:29]
You could also see it as they contain a key that is the empty string, and the value is
[00:24:34]
also the empty string.
[00:24:36]
So you have like an empty parameter between each ampersand.
[00:24:42]
But that's not really useful.
[00:24:44]
So a list of dict key is empty string and value is a list of five empty strings.
[00:24:51]
Perfect.
[00:24:52]
Love that.
[00:24:53]
So how many ampersands do you need to have so that ampersand becomes a key?
[00:24:59]
Right.
[00:25:01]
I do support like ampersand equals ampersand, which is like key is empty string, value is
[00:25:09]
empty string.
[00:25:11]
Because that's what the JavaScript version did and that's what WG spec said.
[00:25:22]
That seems very wise to use like these sort of JavaScript standards because there are
[00:25:29]
just so many people using this that it supported their main use cases.
[00:25:33]
So it seems like a smart thing to piggyback on.
[00:25:36]
I mean, as far as like the elm URL type being extremely unopinionated and as you say, just
[00:25:44]
saying there is no standard, so here's a string.
[00:25:47]
It seems reasonable because if it didn't do that, then you get sort of backed into a corner
[00:25:53]
as a user where those use cases are impossible.
[00:25:56]
Whereas if your user land package, elm app URL has a strong and very reasonable opinion
[00:26:05]
about that, then people don't need to use it if it doesn't support their use case.
[00:26:09]
So I think it's a nice state of things.
[00:26:11]
And it's also kind of nice that elm doesn't have any built in assumptions about you using
[00:26:17]
the official core elm URL package, except for the URL type, which is just the sort of
[00:26:25]
raw URL value.
[00:26:26]
You don't need to use a URL parser if you don't want to, which is quite nice.
[00:26:30]
That is pretty nice.
[00:26:32]
So if you wanted to grab those query parameters and parse them into your route in your sort
[00:26:39]
of parsing function that you write as a user, pulling off values from your app URL record,
[00:26:46]
what would that look like?
[00:26:48]
There are two different use cases for query parameters, in my opinion.
[00:26:54]
Either you have just a couple of them that you support.
[00:26:58]
You could have one called sort to decide in which way a product listing is sorted and
[00:27:06]
one called size, if you want to filter your products by size and so on.
[00:27:13]
Or you could have a more dynamic use case where you kind of want to take all of the
[00:27:17]
parameters as a dictionary, look them up dynamically according to something defined in the backend
[00:27:27]
or whatever.
[00:27:28]
So that latter use case where you take all of them, that's quite easy.
[00:27:31]
You just take URL dot query parameters and then off you go.
[00:27:37]
But if you just want to pluck off a couple of them, then in this function where you do
[00:27:44]
your pattern match on the path, we talked about what you should return.
[00:27:50]
Should you return a route or should you return a maybe route?
[00:27:55]
And I recommend returning a maybe route because that fits really well with doing dictionary
[00:28:01]
lookups in this query parameters dictionary.
[00:28:04]
So then you could do like dic dot get and then the name of the parameter like sort or
[00:28:11]
size.
[00:28:13]
And then you say URL dot query parameters and then you get maybe list of strings because
[00:28:19]
there's nothing stopping you adding like multiple sort parameters in the URL.
[00:28:24]
And then my recommendation is to always pipe to maybe and then list dot head, which is
[00:28:31]
a somewhat convenient way of just deciding that, okay, we're going to take the first
[00:28:37]
one in case there are multiple.
[00:28:39]
Now finally, I have this maybe string, which is like the sorting order or whatever.
[00:28:45]
And you can choose what you want to do with that string.
[00:28:48]
You could like store it in your route or page type, or you could parse it further.
[00:28:54]
You could switch on it or case on it.
[00:28:58]
Is it the string descending?
[00:29:00]
Then it's okay.
[00:29:01]
Is it ascending?
[00:29:02]
It's okay.
[00:29:03]
If it's something else, then you could like decide to drop it or whatever makes sense.
[00:29:09]
Right.
[00:29:10]
You could turn it into a default value.
[00:29:11]
If you needed a default value, you could take it from a string to a custom type if you wanted
[00:29:18]
to have ascending and descending as custom types.
[00:29:21]
And in which case you have to say, if it's something I don't understand, then probably
[00:29:27]
go with the default option because URL parsing is a little bit interesting because the concept
[00:29:31]
of parsing implies that something could fail to parse, in which case there are some errors.
[00:29:40]
But there's not really a way to show errors to the user.
[00:29:44]
At least that's not usually what you want.
[00:29:46]
Like if you say sort equals D, but it's supposed to be D-E-S-C, you probably just want to ignore
[00:29:56]
that.
[00:29:57]
Although I suppose if you wanted to like parse into a result type and say something that's
[00:30:02]
wrong with the error, you could.
[00:30:04]
But I guess the way users are using the URL is they're usually not hand editing it.
[00:30:11]
So you don't necessarily want to give them error feedback.
[00:30:13]
They're like clicking links.
[00:30:15]
So you just want to assume they're probably going to have a valid URL and just fall back
[00:30:20]
if not.
[00:30:21]
It's only power users that edit URLs, I think.
[00:30:26]
So I think it's fine to drop bad values and like arbitrarily choose to take the first
[00:30:32]
one if there are duplicates, because power users will like understand that, hmm, okay,
[00:30:38]
it's ambiguous if I do two of these.
[00:30:41]
So it doesn't really matter what you do.
[00:30:44]
It is kind of cool how this pattern is like, the code is so simple and easy to understand.
[00:30:51]
Like in my opinion, it's easier to understand than an Elm URL parser.
[00:30:55]
Not even thinking about like the parser type with the two type variables, like the code
[00:31:01]
itself is easier for me to follow.
[00:31:04]
But at the same time, it's more powerful.
[00:31:08]
Like for example, if you wanted to parse it into a result type and give some error with
[00:31:13]
messages saying, you know, sort was an invalid value, and then send that in your bug tracker
[00:31:21]
to just say like, just for your information, this is something that went wrong in the URL,
[00:31:26]
and then fall back to no URL, you could do that.
[00:31:29]
Whereas I wouldn't know what to do with the Elm URL parser for a more sophisticated use
[00:31:35]
case.
[00:31:36]
But like really, my imagination just starts firing at all these possibilities with this
[00:31:40]
simple pattern.
[00:31:41]
I remember that with Elm URL, you can put parsers for the query using the operators.
[00:31:53]
So you have this less than question mark, greater than symbol or operator, and then
[00:32:00]
you can put a query parser, right?
[00:32:03]
And I'm guessing that you can map that one, you can use parser and then, and that means
[00:32:08]
that you can fail the query or the felt this parsing, if it doesn't match descending or
[00:32:14]
ascending, right?
[00:32:16]
Yeah, that's totally possible.
[00:32:18]
Yeah.
[00:32:19]
But the thing is, usually you do parser that one off.
[00:32:22]
So if this thing fails, then it just goes on and tries a following parser.
[00:32:27]
And in which case you don't have an error message, because you just says, okay, well,
[00:32:32]
this one doesn't match.
[00:32:33]
Let's go to the next one.
[00:32:34]
So yeah, with with an Elm app URLs approach, it is easier to make nicer error messages,
[00:32:42]
if you want to.
[00:32:43]
I could also imagine I'm trying to think of a use case where you would want this, but
[00:32:48]
I could imagine combining certain combinations of query parameters with with path segments
[00:32:55]
to say, these two combinations, go together and kind of pull out the the maybes from from
[00:33:02]
types and turn them into more nuanced variants that they tell you exactly the types that
[00:33:09]
go together.
[00:33:10]
Of course, like, again, the problem is that you don't want URLs to be able to fail easily,
[00:33:15]
you want them to be very resilient and fault tolerant.
[00:33:19]
So usually, we, we actually do end up having maybes often in things like query parameters,
[00:33:26]
because the segments need to match us, we tend to be strict about segments and say,
[00:33:32]
hey, if, if you go to slash products with a Z, instead of products with an S, then we're
[00:33:39]
not going to handle that.
[00:33:40]
But with query parameters, you want to just gracefully fall back to default.
[00:33:45]
So yeah, for instance, I can imagine that in some cases, that's not true.
[00:33:51]
For instance, like we like to say product slash and then a product slack or product
[00:33:55]
ID, but you can also have imagine that there's a query parameter for the product ID or for
[00:34:03]
a user.
[00:34:04]
So imagine you have slash user, question mark, ID equals something.
[00:34:10]
And that like, for some reason, like maybe legacy reasons, because this needs to support
[00:34:15]
some URL pattern that was meant that was designed years ago, like this ID should never be absent.
[00:34:23]
So if you don't have it, then you probably want to lead the user to an error page saying,
[00:34:29]
hey, the ID is necessary.
[00:34:32]
And that would be easier with Elm app URL compared to Elm URL.
[00:34:36]
So another thing, I've thought a lot about URL parsing stuff in the context of Elm pages,
[00:34:44]
Elm pages has file based router and, and Elm, Elm pages supports sort of like splat routes
[00:34:53]
like catch all routes and, and optional route segments.
[00:34:57]
As I was designing, you know, the approach for that, one of the things I was looking
[00:35:03]
at with like the Elm URL parser was, well, how would I do catch all routes.
[00:35:08]
And what I ended up realizing is that it doesn't support catch all routes.
[00:35:13]
And so essentially, like under the hood for for the file based router I built into Elm
[00:35:18]
pages, I was not able to use Elm URL to parse the URLs because it didn't support what to
[00:35:24]
me seems like this very common standard use case of having a catch all route like having,
[00:35:31]
you know, github.com slash repo, user slash repo slash branch slash blob slash some file
[00:35:41]
path, which is a, an arbitrary number of segments that seems like very normal.
[00:35:47]
But with your pattern in Elm app URL, it's pretty trivial, you just use these standard
[00:35:54]
pattern matching tools of list pattern matching.
[00:35:58]
That is a good point.
[00:35:59]
I'm not sure if I've ever made an app with like any number of segments at the end or
[00:36:05]
something.
[00:36:06]
But now that you say it, I wouldn't know how to do it with the URL parsers.
[00:36:11]
I think you would have to go back to the URL type, right and do it yourself.
[00:36:17]
Yep, that's that's a that's a good experience.
[00:36:20]
Yeah, I actually so I think I saw Simon posting something maybe this was like the seed of
[00:36:27]
this design for you.
[00:36:28]
But I think I saw you posting in response to somebody's question on slack about Oh,
[00:36:34]
you could actually like, handle URL parsing with this simple trick, you could just do
[00:36:38]
a simple pattern match.
[00:36:39]
And I was like, Oh, yeah, you could couldn't you.
[00:36:42]
And then I quickly went in and cut out all of these regexes from the Elm pages generated
[00:36:49]
code for URL parsing and just turned it into a pattern match in the generated code.
[00:36:54]
And it's great.
[00:36:55]
So thanks for that.
[00:36:57]
So one thing where I think Elm app URL is maybe not as nice or like, it chooses a different
[00:37:06]
way of doing things.
[00:37:07]
In Elm URL, you could have segments, which are parsers, right?
[00:37:11]
And you can map them, right?
[00:37:13]
So you just like JSON decoding, you can map it, you just like maybe you can map it.
[00:37:20]
And for instance, you could have a custom type for a product ID or product slug, which
[00:37:26]
would not just be a string.
[00:37:28]
And that you would have to do yourself in the patterns in the pattern matching, right,
[00:37:33]
you would extract the product slug, and then it's just a string.
[00:37:38]
So you would need to not forget to convert it to a product slug.
[00:37:43]
That is correct.
[00:37:44]
Except that you can't really forget it, since it won't compile if you exactly type.
[00:37:50]
But yeah, you would need to do it wrongly in a few places, like in the type in the route.
[00:37:56]
And yeah, so that is a good point.
[00:38:00]
Yeah, I don't think it's a big deal breaker at all.
[00:38:03]
Like, yeah, not at all, actually.
[00:38:06]
My take on it is that so Elm URL, it has parsers for string and int by default, and then you
[00:38:13]
can make your own.
[00:38:14]
And in my experience, I use string almost all the time, int very rarely.
[00:38:22]
And when I use a string, it's usually like, this is supposed to become a product ID or
[00:38:28]
a product slug or a user ID or something.
[00:38:31]
And they are like, opaque anyway, I don't need to like do much to turn a string into
[00:38:36]
a potential product ID.
[00:38:39]
All I need to do, basically is to wrap it in a type.
[00:38:43]
And of course, that might not be a valid product ID.
[00:38:47]
But you'll notice that very quickly, because the first thing you're going to do on your
[00:38:50]
product page is trying to fetch that product.
[00:38:53]
And if that gives a 404 from your API, then you need to display like a product does not
[00:38:59]
exist page.
[00:39:00]
Yeah, wait a second, like, so if you say that the product slug or product ID is an integer,
[00:39:08]
and it starts with 000, and it's like 0001234.
[00:39:13]
And you say it's an int, then the product ID is 1234 and not 0001234.
[00:39:21]
So when you go out and ask the server, like, can you give me the product, it will say like,
[00:39:28]
I don't know this one.
[00:39:29]
I know one that starts with 000.
[00:39:31]
But I don't know 1234.
[00:39:34]
So that's like, kind of scary, actually.
[00:39:37]
Actually unsure what the int parsers in Elm say about leading zeros.
[00:39:43]
But the point here is that if you are typing your IDs as ints, then you could ask yourself,
[00:39:51]
why am I doing that?
[00:39:52]
Why does it matter that it's an int?
[00:39:54]
You're not supposed to do math with IDs, you're supposed to like just check are things equal
[00:39:59]
and stuff like that.
[00:40:01]
Unless your IDs are sequential, which for security reasons, you shouldn't do anyway.
[00:40:07]
Unless they're Pokemon.
[00:40:09]
The Pokedex number is a meaningful number.
[00:40:13]
Fair, fair.
[00:40:17]
Should we go ahead and talk about the third of my issues list, which is the escape incident.
[00:40:26]
The escape incident.
[00:40:27]
Is this also related to Jurassic Park?
[00:40:29]
Yeah, the dinosaurs escape.
[00:40:32]
That's true.
[00:40:35]
So in the Elm URL package, there is a module called URL.builder.
[00:40:42]
And it has functions like the one I use the most called absolute, and there are like relative,
[00:40:48]
I think, and cross origin and stuff like that.
[00:40:51]
But they're pretty similar, all of them.
[00:40:53]
They take two lists.
[00:40:55]
One list is for the path, and one is for parameters.
[00:41:00]
And if we just focus on the first list, which is the path, then you can write something
[00:41:06]
like the string A comma the string B.
[00:41:10]
The result will be slash A slash B, which is quite handy for generating URLs.
[00:41:16]
But if you type a slash inside of one of those strings, what is going to happen?
[00:41:25]
What should happen?
[00:41:28]
What should happen is it should get URL encoded.
[00:41:31]
But I'm guessing that's not what happens.
[00:41:34]
Exactly.
[00:41:35]
It is not.
[00:41:37]
And that is usually fine.
[00:41:41]
But it can also result in some really weird code.
[00:41:43]
I've seen people using this absolute function where they have written one segment as a string,
[00:41:53]
like A by itself, and then the next hardcoded string, B, and then maybe comma and a variable,
[00:42:02]
which is something dynamic, and then comma, more hardcoded things.
[00:42:07]
But this time, they chose to use slashes inside the string instead.
[00:42:11]
And at the very end, they even do plus plus something other dynamic, which is like now
[00:42:17]
you have mixed all the different ways that you can.
[00:42:20]
Oh, I don't like that.
[00:42:24]
But that's just, it's fine.
[00:42:26]
It's just confusing code.
[00:42:28]
But it could have more severe consequences if, for example, if you have like a URL slash
[00:42:38]
blog slash a slug of a blog, and your blog post is called like A B testing.
[00:42:46]
And that's usually written as A slash B testing and your slug function or whatever, allows
[00:42:54]
slashes in a slug, which might be pretty uncommon, but let's pretend that happened.
[00:43:00]
If you then try to create a URL, and you put that slug in there, then you're accidentally
[00:43:06]
gonna create a URL with three segments instead of two like you expected, which probably results
[00:43:12]
in a 404 if you try to use that link.
[00:43:14]
So the approach I wanted to take with the app URL is that it should feel more like the
[00:43:20]
HTML package.
[00:43:22]
When you put in a string in HTML in Elm, I never think about what that string looks like,
[00:43:27]
like, will there be less than signs?
[00:43:29]
Will there be ampersands?
[00:43:31]
Could this be treated as HTML?
[00:43:32]
No, it never happens.
[00:43:35]
So I make sure that you can put any string anywhere, and it will represent that string
[00:43:42]
exactly and not be treated as URL syntax.
[00:43:48]
Okay, so if you if you put a slash in A B testing, then the slash would be escaped as
[00:43:55]
a percentage sign and something else.
[00:43:58]
Exactly.
[00:43:59]
Okay, that feels very Elmy to me in the best possible way like it because to me like the
[00:44:04]
feeling of working in Elm that I love is number one, like, not feeling like there are foot
[00:44:13]
guns all around me that I'm going to set off by mistake, like just that peace of mind of
[00:44:18]
using something and being like, yeah, it's going to be fine.
[00:44:21]
And then secondly, when I'm trying to like follow a code path and understand what it's
[00:44:26]
doing, or what it might do, or what might be causing a bug, having fewer places to look
[00:44:32]
for, for the source of the bug, fewer special cases to think about, or like fewer possibilities,
[00:44:38]
like the type is more narrowed, or what this can possibly do is more narrowed, or side
[00:44:43]
effects can't be coming from here.
[00:44:44]
So this is like, could this segment that's coming from user input or something be causing
[00:44:49]
this weird URL thing?
[00:44:50]
Like, no, it's like, it's going to be escaped.
[00:44:53]
So you can just cross that off the list of things to look for for a specific type of
[00:44:58]
problem from.
[00:44:59]
Yeah, very, very nice.
[00:45:01]
And this escaping stuff was actually useful at work.
[00:45:05]
We had a bug, like before we used app URL, where we have like a search function in our
[00:45:13]
app.
[00:45:14]
And you can search by a person's phone number, for example.
[00:45:18]
And someone wanted to search for their like full phone number with the counter code, which
[00:45:24]
starts with a plus.
[00:45:25]
So in Sweden, it's like plus four, six, and then the real phone number.
[00:45:30]
And that always gave zero matches.
[00:45:32]
And we were like, why doesn't that work?
[00:45:35]
What's happening here?
[00:45:38]
And when you, we send the phone number in a query parameter, and we were using the standard
[00:45:44]
elm slash URL, like URL dot builder dot absolute.
[00:45:51]
And then the second list of that function, you can specify your query parameters.
[00:45:55]
It does not escape the plus.
[00:45:58]
And for some reason, only in query parameters, a plus means space.
[00:46:02]
So our server, by default, just unescaped that for us and says like, oh, it's a space
[00:46:08]
for six and the phone number.
[00:46:10]
And we don't have any phone number that starts with a space.
[00:46:15]
So we had to like in the elm code go and manually use the URL dot percent and code function
[00:46:21]
to make sure it's escaped.
[00:46:23]
But now with app URL, we don't need to think about it.
[00:46:26]
We could like remove that and just put the phone number in as is and it's going to work.
[00:46:31]
Perfect.
[00:46:32]
So when you mentioned that there's an absolute and a relative function in your builder, you
[00:46:39]
said the second argument is a list.
[00:46:41]
And I was like, why is it a list?
[00:46:44]
Because the in the URL, the query is just a string, right?
[00:46:48]
So why for query builder, do we have a list?
[00:46:52]
So it looks like it's taking a list of query parameters.
[00:46:55]
And those query parameters are basically key values, like you can say string, which with
[00:47:01]
a key and a value, which is a string, or int with a key again, and an int value.
[00:47:08]
So like, it's weirdly, the writing of the building of a URL doesn't match how it is
[00:47:17]
parsed.
[00:47:18]
So that's kind of weird.
[00:47:19]
I agree.
[00:47:21]
Parser and builder are very, like almost different universes in the same package.
[00:47:27]
Yes.
[00:47:28]
So the URL builder is like, oh, yeah, sure.
[00:47:32]
Of course, queries are like, they're a dictionary and for parsing, like, no, it's just a string.
[00:47:38]
People can do whatever.
[00:47:39]
Okay.
[00:47:41]
And the fun thing with a parser is that, like when you run a parser, you give it a URL,
[00:47:48]
the URL type.
[00:47:49]
And the first thing it does is pre-process the query parameters into like a dictionary
[00:47:55]
kind of structure.
[00:47:57]
And then the parsers that you write, like work on that pre-parsed type.
[00:48:04]
So it's like, couldn't really decide which way to go or I don't know.
[00:48:09]
Yeah.
[00:48:10]
And also when you when you think about the URL builder in the elm-url package, and the
[00:48:18]
way you create these query parameters, there's like a query parameter type, which you have
[00:48:23]
a list of.
[00:48:24]
And if it's a string, then you give string with the key and the value.
[00:48:29]
If it's an int, you give an int with the key and the value.
[00:48:32]
But so what is the like query parameter type and the int and string builders for the URL
[00:48:40]
type, query parameter type giving you?
[00:48:42]
It's really giving you string.fromInt.
[00:48:46]
That's what it's bought you.
[00:48:47]
So like, it seems so much more natural to just remove that level of abstraction.
[00:48:53]
It feels kind of similar when I use like the headers in elm-http.
[00:49:01]
And you build up a list of headers, but you have to do a header constructor to do, you
[00:49:07]
know, http.header and give it the key and value.
[00:49:11]
Why can't I just give it a list of tuples?
[00:49:14]
Like that would have the same effect.
[00:49:16]
So it just seems like, again, the Jurassic Park principle here.
[00:49:21]
It's like, you could do that, but what value is it really giving you?
[00:49:26]
And it's that much more that you have to like, look up and hold in your head when you're
[00:49:30]
navigating this API.
[00:49:32]
So I really like the simplicity you've arrived at.
[00:49:36]
I'm very curious.
[00:49:38]
I'm sure it's difficult to answer as a package author because you do your best to not have
[00:49:44]
any cons.
[00:49:45]
But we've talked about a lot of pros.
[00:49:47]
Are there cons?
[00:49:48]
Are there downsides?
[00:49:49]
Are there common use cases that your package might not be a good fit for?
[00:49:53]
I think that if you use query parameters like a lot on every page, quite a lot of them,
[00:50:00]
it might be annoying to work with a dictionary type that I went with.
[00:50:06]
And my reasoning when designing the query parameters was that like in none of the apps
[00:50:13]
that I work with, we have that many query parameters.
[00:50:16]
So it was hard to come up with something nice because I use it so little that like just
[00:50:23]
a little bit of extra code here and there was simpler than trying to figure out a nice
[00:50:29]
API for working with them.
[00:50:31]
So basically the thing that I'm the most excited about in the package is the pattern matching
[00:50:39]
on the path pattern, which is easy with this package.
[00:50:42]
And then the query parameters that I just wanted to like, here is a structure that represents
[00:50:49]
them quite well.
[00:50:51]
And it's kind of easy to work with, but it might require a line or two or extra of extra
[00:51:00]
code here and there.
[00:51:01]
Yeah, I mentioned that you rarely use query parameters in practice, but also even less
[00:51:09]
multiple times the same one.
[00:51:11]
So you have a dictionary of list string as values, but in practice it's always just a
[00:51:18]
single value, right?
[00:51:19]
That must be the most common.
[00:51:21]
Yeah, sure.
[00:51:22]
I actually really like this thing I noticed happening a lot in Elm where something that
[00:51:28]
like in other language ecosystems, I noticed tends to be like a library to help you do
[00:51:36]
something.
[00:51:38]
In Elm, sometimes it's just, well, it's actually pretty straightforward to do it.
[00:51:43]
And it ends up being like a pattern rather than a library.
[00:51:46]
And really it feels like that's what Elm app URL is.
[00:51:49]
It's like a, it's three functions to help you use a pattern, but the pattern is the
[00:51:56]
bigger thing.
[00:51:57]
And if you wanted to, if you have very heavy processing of query parameters in your application,
[00:52:05]
you can create your own domain specific query parameter parsing API and use that with the
[00:52:14]
dict string list string.
[00:52:16]
So it's like ultimate flexibility and simplicity, and you can build your own thing to, you know,
[00:52:23]
to address your own internal needs if you outgrow what this pattern allows.
[00:52:29]
Speaking about patterns, there is one pattern I would like to bring up, which is dry, the
[00:52:34]
dry principle.
[00:52:35]
Don't repeat yourself.
[00:52:37]
I've learned something there when using this package and that is to don't worry too much
[00:52:44]
about repeating yourself in this case.
[00:52:47]
What did you say?
[00:52:49]
Don't worry too much about repeating yourself.
[00:52:51]
Oh, now I did it.
[00:52:53]
Did you set me up?
[00:52:54]
But did you worry about it?
[00:52:56]
You didn't worry.
[00:52:59]
You're good.
[00:53:01]
Oh, nice.
[00:53:03]
So it's very common that you have URLs that all start with the same pieces.
[00:53:12]
Like you could have slash product slash slug.
[00:53:15]
And on top of that, you could have like slash specs, slash reviews, slash whatever.
[00:53:22]
And for reviews, maybe have like slash reviews slash and then review ID or something.
[00:53:28]
And you might be tempted to like write your code so that you mentioned the string product
[00:53:34]
just once or like you mentioned the whole piece product slash slug slash reviews just
[00:53:42]
once so that if you need to change that URL, there will be just one string to change.
[00:53:48]
But when I have written my code like that, like nested pattern matching or like having
[00:53:54]
sub functions for parsing deeper and deeper, that's so much harder to understand and results
[00:54:00]
in so much more code.
[00:54:02]
So what I like to do is to simply type out every pattern in my pattern match, even if
[00:54:09]
it means I write product like 10 times, it's so much easier to read.
[00:54:13]
And you also get this like nice overview of all your pages in one place.
[00:54:18]
And what I'm thinking here is that you need to repeat it once anyway, because you're going
[00:54:25]
to need to be able to create these URLs as well.
[00:54:28]
So you can't just mention product, the string product in a pattern match, you also need
[00:54:34]
to have it in a function that can create those URLs.
[00:54:38]
And on top of that, like, how often do you change your URLs?
[00:54:42]
It's usually an anti pattern to change your URLs, but because people are going to have
[00:54:47]
them in their history and stuff, and if they don't work, your, your app is broken, right?
[00:54:51]
Yeah, if you're if you're really worried about it, maybe write some unit tests for it.
[00:54:58]
There's someone named Arlo Belshi has a concept he talks about, I'll link to the blog post
[00:55:05]
where he makes the argument that for for tests that you write, people overuse the concept
[00:55:12]
of dry, and he proposes for testing using a concept called wet, which he he says is
[00:55:21]
write explicit tests.
[00:55:23]
Because sometimes what happens is, you almost need a test for your tests, because you use
[00:55:29]
so many levels of abstraction for writing your tests that you don't know if it's actually
[00:55:35]
doing what you expect.
[00:55:36]
So his argument is, you should be able to look at a test and know exactly what it's
[00:55:41]
doing.
[00:55:42]
And having extra boilerplate and repetition is not a bad thing if it makes it very easy
[00:55:50]
to understand exactly what it's doing without having to second guess it.
[00:55:53]
And I think a similar concept would apply here.
[00:55:56]
Don't be too clever, you know, and especially don't be too clever where it doesn't matter,
[00:56:01]
which URLs is a good example, in my opinion, because like, how many URLs are you going
[00:56:08]
to have in your app?
[00:56:09]
In every app that I worked with, it's like 10 or 20 or 30.
[00:56:14]
But we don't add a new one like every day.
[00:56:17]
So to me, it makes sense to have like a dumber, simpler solution for such things.
[00:56:24]
Yeah, actually, Elm has changed my understanding of what maintainable code looks like.
[00:56:31]
And I something is stuck with me that I heard Richard Feldman say about, like, how we talk
[00:56:36]
about boilerplate in these things.
[00:56:39]
And of course, like, there's certain types of boilerplate that make code harder to understand
[00:56:44]
and maintain.
[00:56:45]
And that's not good.
[00:56:46]
You know, you want code to be very straightforward and clear and easy to understand.
[00:56:51]
And you want things to be less error prone and fewer places for human error.
[00:56:57]
But boilerplate that doesn't have the possibility of human error that doesn't introduce another
[00:57:03]
possible point of failure is not such a bad thing.
[00:57:06]
It's not necessarily what's slowing down your ability to maintain code.
[00:57:12]
And so that's kind of stuck with me is like, when you're trying to simplify something,
[00:57:17]
you have to think about like, why are you simplifying it for some aesthetic purpose
[00:57:22]
that doesn't actually change how easy something is to maintain and work with and how bug prone
[00:57:27]
it is or not, you know, so I think that's a that's a really good nugget of wisdom.
[00:57:33]
What really surprised me with this solution with LmapURL is how simple it is.
[00:57:39]
And then I'm wondering, like, why did we put up with LMURL's approach for so long?
[00:57:46]
Like, I mean, it works.
[00:57:48]
So it's not something that we have to reinvent on purpose, because maybe it works pretty
[00:57:54]
fine.
[00:57:55]
But like, how, how come we stuck with it for so long?
[00:57:59]
I think the key here is that the elm slash URL package works.
[00:58:05]
There are like some annoyances with it, but it doesn't stop you from doing what you're
[00:58:10]
supposed to.
[00:58:11]
And once you are a bit familiar with elm, like you figure it out, you're able to set
[00:58:19]
up the URLs you need.
[00:58:21]
And then you don't think about it and move on with more interesting things.
[00:58:24]
Yeah.
[00:58:25]
And also, it's a core package, right?
[00:58:28]
So, oh, it has to be good or this has to be the way.
[00:58:33]
Yeah.
[00:58:34]
I guess that's like judging by the quality of elm itself and all the core packages, you
[00:58:41]
have that expectation on all of them.
[00:58:43]
But if you think about it, like, of course, there's going to be one package that isn't
[00:58:47]
as good as the others.
[00:58:49]
It's like impossible to be perfect in every single one of them.
[00:58:53]
And it took us long to realize, I guess.
[00:58:57]
But I bet that beginners might appreciate a simpler way because that is like one less
[00:59:03]
hurdle to do an elm application.
[00:59:07]
Right.
[00:59:08]
So in your opinion, should we get rid of the two operators that elm URL defines?
[00:59:13]
Yes.
[00:59:14]
I think that if you take the stance that there shouldn't be custom operators, then there
[00:59:21]
shouldn't be any operators to import from any package.
[00:59:26]
It's like a weird extra thing to learn that, oh, wait, what?
[00:59:30]
In the URL package, you can import operators?
[00:59:33]
What does that mean?
[00:59:34]
Like, it's so foreign.
[00:59:36]
You're not used to from other languages that you can import an operator.
[00:59:41]
They just exist.
[00:59:44]
And it feels like if we're going to have extra custom operators, don't waste them on URL
[00:59:50]
parsing.
[00:59:51]
Like, you could choose anything and you chose URLs.
[00:59:55]
That's not what I would do.
[00:59:57]
I'm guessing the only remaining ones that are not in the basics module are the ones
[01:00:03]
from URL parser.
[01:00:06]
Just parser.
[01:00:07]
Just parser.
[01:00:08]
Elm parser.
[01:00:09]
Yeah.
[01:00:10]
Yeah.
[01:00:11]
Which I've always thought kind of trips people up a little bit.
[01:00:14]
And I've always thought, how about keep and skip for those two operators?
[01:00:20]
Because it says what it does explicitly, and it reduces having to explain some cute thing
[01:00:27]
about an animal like eating your parsing input or something.
[01:00:32]
I just think it would, I think it reads more easily and intuitively, and it would kind
[01:00:37]
of ease the learning curve there a bit.
[01:00:39]
And it would reduce the number of custom operators in published packages.
[01:00:44]
Also like just the, sometimes you use pipes, and sometimes you use those symbols that look
[01:00:50]
like pipes, but the way that you have to move those instructions, like that there, it's
[01:00:56]
a bit weird.
[01:00:57]
Like, oh, if you now want to keep this, then you need to move things around.
[01:01:02]
I found it pretty confusing when I was working with parsers at least.
[01:01:07]
And also like if we can remove the importance of parsers, of operators, that would make
[01:01:12]
my life easier with Elm Review.
[01:01:14]
So I'm fine with that change.
[01:01:18]
So is there where you're going to tackle next parsers?
[01:01:23]
To be honest, like I've used the Elm slash parser package exactly once, I think, in like
[01:01:29]
one Advent of Code puzzle, where I didn't even need it.
[01:01:33]
I just used it because I wanted to try it out.
[01:01:38]
When it comes to like real applications, I've either been using other people's parsers from
[01:01:44]
packages on the packages site, or I've just gone over, or I've just like used a RegEx
[01:01:51]
or string splitting or something, and that has been just fine.
[01:01:55]
Actually RegEx might be next on the chopping block, in my opinion, of something.
[01:01:59]
I think there's a saner way to do a RegEx API in Elm.
[01:02:04]
That's something in between needing a full on parser package and needing to use some
[01:02:09]
kind of loosely typed wrapper around the JavaScript RegEx API.
[01:02:15]
I have some thoughts there.
[01:02:18]
That's for a different episode.
[01:02:20]
That might be for a different episode.
[01:02:21]
But Simon, this is a very interesting package, and it's really unique in the sense that it
[01:02:27]
is seems like far more design attention and documentation and examples than code, which
[01:02:34]
is kind of cool.
[01:02:36]
What was it like designing this package?
[01:02:39]
Was it just having a bunch of open tabs and doing a bunch of reading and research?
[01:02:43]
A lot of that and a lot of, I mean, it actually took many months because I started using it
[01:02:52]
or like trying it in a branch at work.
[01:02:56]
And then every time I was like, no, now I have the API nailed.
[01:03:02]
And then I tried to use it and like, nah, this was a bit annoying.
[01:03:06]
Or like, I didn't think about this use case.
[01:03:10]
So I think the key was to actually use it in several real projects to learn what is
[01:03:18]
important and what is easy to use.
[01:03:21]
Did the scope of it and the amount of code shrink over time?
[01:03:25]
I get the sense that maybe you realized how simple it could be over time, or did you kind
[01:03:30]
of have that sense from the beginning?
[01:03:33]
It changed a bit over time.
[01:03:34]
I like removed some query parameter helper function that I realized didn't really improve
[01:03:41]
things.
[01:03:44]
And I got some feedback on Discord and that helped a lot too.
[01:03:50]
For the query parameters, was it like piping into list.head?
[01:03:55]
That idea that how do you get like a single value for the query parameter?
[01:04:00]
Yeah, I was debating if I should have a function for getting just a single value since that
[01:04:06]
is the most common use case.
[01:04:09]
But it was very difficult naming it and also deciding which way should it work.
[01:04:15]
Should I take the first one?
[01:04:17]
And I looked at like some other programming languages.
[01:04:19]
I think I don't remember which one it was that took the first one, maybe Go?
[01:04:24]
Or should I take the last one?
[01:04:26]
That's what Django in Python does.
[01:04:30]
Or there is actually one more option, which is what elm.slash.url does.
[01:04:33]
If there is more than one, you get none of them.
[01:04:37]
Really?
[01:04:38]
I'm not sure like what the intention is, if it's a mistake or if it's like, this is ambiguous,
[01:04:43]
you get none of them.
[01:04:44]
Right, it failed to parse.
[01:04:46]
That, see, the thing is that if you can present error messages, then that makes a lot of sense.
[01:04:54]
But when you can't, like it seems like one of those instances where you want to be, what's
[01:04:58]
the what's the phrase, you know, like accept a wide range of input, but then be very precise
[01:05:04]
in how you build URLs in your application, but accept a wide variety of possible inputs
[01:05:10]
with possible oddities from the user.
[01:05:13]
Something I realized was that if no one really has noticed that for so many years, I don't
[01:05:18]
think it matters which one you take.
[01:05:20]
You just choose one approach, and that's going to be fine.
[01:05:24]
But in the end, I decided not to make that decision in the package and just promote the
[01:05:30]
pattern of using list.head instead.
[01:05:33]
Yeah.
[01:05:34]
Honestly, if I see a query pattern that is duplicated in my URL, I consider that to be
[01:05:39]
a bug.
[01:05:40]
Like, if I see one, I'm gonna like, yeah, we'll probably need to do something else than
[01:05:45]
that, like, separated by comma, whatever.
[01:05:48]
Yeah, and you have the added benefit of not having an O of N query parameter lookup if
[01:05:54]
you do the last one and someone put 50 query parameters of the same name.
[01:05:58]
So that's good.
[01:05:59]
It's a DDoS attempt factor.
[01:06:04]
Yeah.
[01:06:05]
Well, Simon, what should people look at if they want to get started?
[01:06:10]
I made a little video that very quickly explains the core concept.
[01:06:15]
But other than that, you can go to the package on the package site, the readme, like, shows
[01:06:21]
you the main thing immediately.
[01:06:24]
And then I have some examples for every function as well in the documentation.
[01:06:29]
Amazing.
[01:06:30]
Well, thank you for the thoughtfully designed package.
[01:06:35]
Thank you for the conversation.
[01:06:36]
Thanks so much for coming back on the show.
[01:06:38]
Thanks for having me.
[01:06:39]
It was great.
[01:06:40]
And Jeroen, until next time.
[01:06:41]
Until next time.