spotifyovercastrssapple-podcasts

Make Impossible States Impossible

We discuss two classic talks and how to apply the ideas of Make Impossible States Impossible to your codebase.
September 21, 2020
#13

The talks

Dealing with Impossible States

  • Building up a "truth table" of possible states (see Evan's guide Types as Sets)
  • Which rows are invalid?
  • Create a new type with only the valid ones possible

More references

Transcript

[00:00:00]
Hello, Jeroen.
[00:00:01]
Hello, Dillon.
[00:00:02]
How are you today?
[00:00:03]
I'm doing quite well.
[00:00:04]
How about you?
[00:00:05]
Very good.
[00:00:06]
I had a really nice time doing our book club number two.
[00:00:11]
Not really a book club, I guess more of a video club today, but...
[00:00:16]
Conference club?
[00:00:17]
Conference club, yeah.
[00:00:18]
Rewatching some of these classic talks by Richard Feldman, these really influenced the
[00:00:22]
way that I think about problem solving in Elm, and I think that's the case for a lot
[00:00:27]
of other people.
[00:00:28]
It was fun to revisit them.
[00:00:30]
You want to tell people which two talks we watched?
[00:00:33]
Yeah, so we wanted to talk about making impossible states impossible.
[00:00:37]
And since it looked very much like another of Richard's talk, make data structures, we
[00:00:43]
decided to watch both of them and talk about both of them.
[00:00:47]
Yeah, it really did feel like part two of making impossible states impossible.
[00:00:52]
And in fact, it was very reminiscent of a lot of the things we covered in our opaque
[00:00:58]
types episode.
[00:01:00]
Yeah, definitely.
[00:01:01]
So it was as much about, like in a way, making impossible states impossible felt like how
[00:01:07]
do you model the actual types to avoid states that you don't want to be able to represent?
[00:01:14]
And make data structures felt like how do you design an API that limits how you use
[00:01:21]
those data structures?
[00:01:22]
Because I think that that's something that is not always appreciated about API design
[00:01:28]
in Elm is that the way that you design your types is important, but there's this really
[00:01:35]
interesting relationship between what you expose and make possible through the API.
[00:01:41]
And so there's an intimate relationship between the data type and the API.
[00:01:47]
And the API allows you to make guarantees because you have a single gatekeeper that
[00:01:55]
can enforce certain invariants and properties about those data types.
[00:02:00]
So there's an intimate relationship between the data type and the API.
[00:02:04]
Yeah.
[00:02:05]
And as you said, we talked about that during episode two about opaque types.
[00:02:10]
And yeah, without any regards to how things are implemented under the hood, which is not
[00:02:15]
something that you can see from an API perspective.
[00:02:20]
Okay.
[00:02:21]
So we always like to start with the definition you're in.
[00:02:24]
Yeah.
[00:02:25]
How about a definition of impossible states?
[00:02:27]
What is an impossible state?
[00:02:30]
And what does it mean to make an impossible state impossible?
[00:02:32]
Okay, an impossible state is pretty much the kind of states or combination of values that
[00:02:40]
you do not think you can get into.
[00:02:43]
Is where you would add a comment saying, this should be impossible.
[00:02:48]
This is just to make the compiler happy.
[00:02:51]
So usually it's because there are two fields or two data points that conflict with each
[00:02:57]
other in ways that should not be possible.
[00:03:01]
Like you reference something in something that is empty or...
[00:03:06]
Yeah, if you have data types that can sort of vary independently, but it's actually connected
[00:03:14]
the way that it should vary, then for example, Richard was talking about if you just start
[00:03:21]
introducing a maybe, maybe, maybe, maybe, maybe, and these different maybe values, actually,
[00:03:28]
if one of them is present, then the other one must be present.
[00:03:32]
Yeah.
[00:03:33]
Or vice versa.
[00:03:34]
So there's this relationship between when you should have one or the other.
[00:03:38]
And coming from a JavaScript background, I think a lot of people are used to building
[00:03:43]
up data structures in that style.
[00:03:46]
I think TypeScript maybe is starting to change that and change the way people think.
[00:03:52]
Because in JavaScript, there's no reason not to do it that way.
[00:03:54]
Because you can't have the same guarantees that you have in Elm through type design.
[00:04:00]
And TypeScript can allow that.
[00:04:03]
Exactly.
[00:04:04]
Because you're sort of like, you know, there's this dance you do with the compiler where
[00:04:09]
you prove something to the compiler.
[00:04:11]
And once you've proven it to the compiler, you don't have to keep proving it.
[00:04:15]
Whereas in just vanilla JavaScript, you don't get that compiler supporting you.
[00:04:20]
Once you've proven it, you don't get the compiler helping you out and saying, I got your back,
[00:04:25]
you proved it to me.
[00:04:26]
Now you can just work with it.
[00:04:28]
Yeah.
[00:04:29]
The compiler is very unhelpful in JavaScript.
[00:04:33]
If we even admit there's a compiler, which no, there's not.
[00:04:38]
Yeah.
[00:04:39]
So it fundamentally changes.
[00:04:41]
Right.
[00:04:42]
Right.
[00:04:43]
So just having like really a type system and an expressive type system like Elm's, it changes
[00:04:48]
the way you think about modeling your types.
[00:04:53]
And I really like this approach.
[00:04:54]
You know, I'll sometimes actually just go through and use like this sort of, I think
[00:04:59]
of it as like a truth table technique for describing possible states.
[00:05:04]
So maybe we should describe that because I think that's one of the most helpful techniques
[00:05:09]
if you're first learning about making impossible states impossible is just, I mean, just get
[00:05:16]
a pencil and paper and draw a table of all the possible states.
[00:05:21]
Yeah.
[00:05:22]
And if you're used to Boolean logic, that might be the most helpful version of it.
[00:05:29]
Yes.
[00:05:30]
That's what I did at university.
[00:05:32]
So you want to get the logic class.
[00:05:36]
Yeah.
[00:05:37]
Logic classes.
[00:05:38]
You want to get the truth table of A and B. So you make a table with the values of A on
[00:05:45]
top with true and false and B on the sides with true and false.
[00:05:52]
And in every square you say, what is value of A and B when A has this value and B has
[00:05:59]
that value.
[00:06:00]
And that's kind of what you want to do when you do this kind of type design.
[00:06:05]
Look at, oh, is it possible if this field has this value and this field has that value,
[00:06:11]
is this a state that is acceptable?
[00:06:13]
Is this a state that I want my application to get into?
[00:06:18]
Oh no.
[00:06:19]
It's not something that should be supported and if that happens, that's a failure.
[00:06:24]
That's a business logic error.
[00:06:26]
And what you're going to try and do is take only the acceptable values and make a new
[00:06:33]
type that only allows those ones.
[00:06:36]
Yeah.
[00:06:37]
I think just that exercise of at least just knowing with your current data modeling, what
[00:06:45]
are the possible states that you could have with that and then just marking with like
[00:06:51]
a red marker, which ones are invalid.
[00:06:55]
That's you're already very close to doing this technique if you're able to do that.
[00:07:01]
And it's not that hard.
[00:07:02]
It's really a habit.
[00:07:03]
But yeah, Evan Chappellicke has an article on the ARM guide called Types as Sets where
[00:07:10]
he basically explains to you how to compute the cardinality of a type, so a cardinality
[00:07:16]
is how many values or states can you represent with this type.
[00:07:23]
So a Boolean can represent two, a unit, a one, never zero, and a list and infinity.
[00:07:31]
And by composing all of those, you can compute how many possible states you will make possible.
[00:07:38]
So what you want is your cardinality of your new type to be the exact same as the number
[00:07:46]
of possible states you want to allow.
[00:07:49]
Yeah.
[00:07:50]
And if you just enumerate those, that really helps.
[00:07:52]
So like one way to think about it is it's like a for each.
[00:07:56]
So for each value of A, I can have this value of B. Like you were talking about a truth
[00:08:01]
table for A and B, two Booleans.
[00:08:05]
Well then you would say for each value of A, so take for true.
[00:08:11]
So for true, B could be true or B could be false.
[00:08:16]
So you're doing a nested for loop conceptually for B. So for A is true, you're doing B is
[00:08:24]
true and B is false.
[00:08:26]
And then for A is false, you're doing B is true and B is false.
[00:08:32]
And now you've got your truth table with four entries.
[00:08:34]
So it's a nested for loop, which means that you're doing the size of the outer, the cardinality
[00:08:41]
of the outer type times the cardinality of the inner type.
[00:08:46]
And if there are more types, times the cardinality of each of those.
[00:08:50]
And yeah, Evan's sort of appendix types of sets describes this really nicely.
[00:08:55]
But that's the general technique.
[00:08:57]
And so I mean, we're talking about Booleans, but if you have a custom type, a Boolean is
[00:09:03]
just a custom type with two variants.
[00:09:07]
And the same would be true for any custom type with two variants.
[00:09:10]
If it's a custom type with three variants, then the cardinality of that is three.
[00:09:14]
Yeah, unless they have arguments, then it gets more complicated.
[00:09:19]
Then it gets more complicated.
[00:09:20]
And I don't think we need to exhaustively describe this technique, but I think that's
[00:09:24]
the mindset of really go through that exercise and enumerate every possible state that you
[00:09:31]
could describe with your data types.
[00:09:34]
And that's a good first step for starting to say, okay, well, which ones do I not want
[00:09:39]
to be able to represent?
[00:09:41]
So why do this?
[00:09:42]
What does it buy you to do this in your code?
[00:09:45]
I find that it makes a lot of things more simple because you won't have to handle the
[00:09:51]
impossible states.
[00:09:52]
The issue with impossible states is that there's no good way to resolve them.
[00:09:57]
There's no good way to display them.
[00:09:59]
There's no good way to do anything with them.
[00:10:01]
You never should have entered that state.
[00:10:04]
So when you make it impossible to have those states, you don't have to handle that.
[00:10:09]
So you usually have a lot less code to handle, a lot less cases to handle.
[00:10:15]
Richard puts a lot of emphasis on not being able to test those cases.
[00:10:22]
You can't even write tests for the cases that you want to be impossible.
[00:10:28]
So not having to write tests is good.
[00:10:33]
In JavaScript, what I usually did is if I had a pure function, is try to explain, to
[00:10:42]
handle cases where I put in wrong types, for instance.
[00:10:48]
So there's a lot of tests that bring no value to the project and just waste my time, both
[00:10:56]
in the code and in the test.
[00:10:59]
But with the Elm type system, you don't have to write those.
[00:11:03]
But making it impossible states impossible brings another layer of tests that you don't
[00:11:09]
have to write.
[00:11:10]
Right.
[00:11:11]
So, to contrast it with the alternative, another way to accomplish that, and Richard talks
[00:11:18]
about that as he was designing Elm CSS, he was having these sort of validations.
[00:11:25]
They could be build time or runtime validations.
[00:11:29]
You could return a result type instead of just a type.
[00:11:32]
But that's a huge win if you can eliminate the possibility of having something go wrong
[00:11:37]
at runtime.
[00:11:40]
It's great that Elm doesn't have runtime exceptions, but a library can feel not very Elm like,
[00:11:47]
even though it's not going to have runtime exceptions.
[00:11:49]
If it's littered with places that can have runtime validation errors, then in a lot of
[00:11:56]
cases, it doesn't feel Elm like.
[00:11:59]
You'll notice a lot of Elm APIs might seem simple on the outside, but a lot of thought
[00:12:04]
has been put into how do we expose an API and types that only make it possible to do
[00:12:12]
what we want to be possible.
[00:12:13]
So we don't have to then validate it and return a result and say, okay, this worked.
[00:12:18]
You were able to define this correctly.
[00:12:19]
Yeah, that's something that we really want to do in the Elm community is just try not
[00:12:25]
to do it the way you do it in other languages where things can fail, things can throw exceptions.
[00:12:32]
Usually that goes through API design where we have to think how to avoid having the user
[00:12:38]
handle the error case, for instance.
[00:12:41]
I thought it was really interesting how Richard was saying that his API design approach has
[00:12:47]
changed and that when he was first building Elm CSS, he was thinking, how can I make this
[00:12:53]
look like a DSL that looks like CSS?
[00:12:59]
I think in the Ruby community, that's very much the ethos is that you try to build a
[00:13:05]
DSL that looks as much like the thing you're trying to describe as possible or that matches
[00:13:10]
the aesthetics of something.
[00:13:12]
Maybe it's not exactly CSS, but it's some form that's inspired by that.
[00:13:16]
Richard very much started with that design sense with Elm CSS.
[00:13:22]
He talks about in his talk flipping that and saying, okay, what if instead of starting
[00:13:27]
with I want this to look how CSS looks, that allowed for all these impossible states.
[00:13:33]
He said, what do I want to be possible and what do I want to not be possible?
[00:13:38]
How would I expose an API that ensures that you can only do the things I want to be possible?
[00:13:44]
I think that's a really great point.
[00:13:47]
It changes the way you approach designing APIs.
[00:13:51]
Just like in test driven development, you're thinking about how do I test this single case
[00:13:56]
first rather than how do I implement it?
[00:13:59]
Your thoughts about testing something and how you would ideally use it when you're writing
[00:14:06]
the test is the thing that influences how you implement it and not the other way around.
[00:14:13]
It's the same thing with this API design technique that you start by thinking about what states
[00:14:19]
do I want to model and have be possible.
[00:14:22]
That is what influences your API design.
[00:14:25]
If you want to make a DSL of CSS in this example, you're going to inherit the same problems
[00:14:33]
that CSS has along with all the problems that Elm has.
[00:14:37]
It doesn't have many, but it does limit what you can do sometimes.
[00:14:41]
If you combine both of them, then the solution is probably not as great as having native
[00:14:47]
CSS files in this case.
[00:14:50]
That's why we usually try to do it the other way around.
[00:14:53]
The ethos of not making a DSL.
[00:14:57]
Then you start with something Elm like.
[00:15:00]
You have the strengths of Elm and you don't inherit the problems from the underlying technology.
[00:15:08]
Elm CSS has got that in the talk.
[00:15:12]
Elm UI is a good example of that.
[00:15:14]
You don't have any of the same issues that CSS has or HTML has.
[00:15:21]
It's really fascinating how the Elm type system and really just the Elm culture and philosophy
[00:15:29]
because I think that the people who we see giving talks, publishing libraries, the way
[00:15:36]
that Evan talks about things really influences the culture.
[00:15:40]
It's fascinating how those things have created a design ethos where people really think about
[00:15:47]
what do I want to be possible to express.
[00:15:49]
I remember Teresa giving a talk about her charting library.
[00:15:56]
She was giving all these interesting points that she learned from researching the history
[00:16:01]
of visualizing things, visualizing data.
[00:16:05]
It was no longer a library for like a charting library.
[00:16:09]
It was a philosophy for how to visualize data.
[00:16:14]
The API design reflected a philosophy of what should be expressible.
[00:16:21]
She talked about how people use pie charts often but the human mind is not very good
[00:16:26]
at parsing what percentage of the pie is taken up in a pie chart.
[00:16:34]
The library doesn't.
[00:16:35]
That's an impossible state in the pie chart.
[00:16:38]
Obviously it's a little bit different than an impossible state in a type but it's the
[00:16:41]
same sort of design sensibility behind that.
[00:16:46]
What I'm finding very curious and amazing in a way is that we can do less in Elm than
[00:16:53]
we can do in JavaScript because JavaScript is dynamic and it doesn't care as much about
[00:17:00]
things as Elm does but still we say as a community, let's do it in the Elm way.
[00:17:08]
The Elm way is usually so much better because of all the guarantees that we get.
[00:17:16]
Someone made an Elm GraphQL implementation, I don't remember who, and people say it's
[00:17:24]
best in class.
[00:17:27]
So there are limitations on what Elm can do.
[00:17:32]
So why is the JavaScript version not better?
[00:17:35]
Well, it's because we get all those guarantees even though we have those limitations and
[00:17:41]
that is just amazing to me.
[00:17:43]
It is fascinating.
[00:17:45]
That's the thing is what makes code interesting isn't what it can do but what it can't do.
[00:17:52]
If code can do anything, that's the least interesting thing you could imagine because
[00:17:59]
that's the building block but how does that shape your programming experience and guide
[00:18:03]
you to a certain paradigm?
[00:18:06]
That's actually what makes it interesting.
[00:18:09]
It's very much like the way that our memories work too is we build a model of our world
[00:18:15]
not by adding more things but by taking things away and that's actually what makes things
[00:18:20]
interesting.
[00:18:21]
I know that there are some people who have like a huge capacity for remembering details
[00:18:26]
and it's actually a burden that they find it difficult to function in society because
[00:18:32]
they can't build a clear narrative of what things mean and so they find it very difficult
[00:18:39]
to learn things and understand things because the process of learning and understanding
[00:18:44]
things is synthesizing, distilling, it's carving away the things that are less interesting
[00:18:51]
and building a map.
[00:18:52]
If you took a map, you took a map of the city you live in and it was just a picture, I mean
[00:19:00]
yeah that's interesting but why do we then build maps if we could just do a satellite
[00:19:04]
image?
[00:19:05]
Well because when you're taking out certain details and highlighting other details, that's
[00:19:10]
what makes things really interesting because you're distilling it down and in a similar
[00:19:16]
way I think that that's what a type system and API design really do is it's distilling
[00:19:21]
something down to the essential things and that's what makes a great developer experience
[00:19:26]
using a tool and that's what makes it really delightful to use something.
[00:19:30]
Yeah, yeah I can imagine a map of the Paris subway with a satellite image like that's
[00:19:39]
just not practical at all.
[00:19:42]
Exactly, exactly even though it's more information rich.
[00:19:46]
It's a superset of the information of the actual subway map you would see that's just
[00:19:52]
a graphic representation but it's less useful.
[00:19:57]
Yeah maybe the bus routes because the subways would actually appear.
[00:20:02]
That would be impressive if you could take a picture of that, yeah we're not that advanced.
[00:20:07]
You can see the subway signs of the entrances but yeah.
[00:20:14]
So when I was designing Elm GraphQL like one of my design goals was and I often make this
[00:20:20]
distinction people think that it's just a way of directly interacting with GraphQL and
[00:20:27]
that it's at the same level of abstraction as a GraphQL request but it's actually not.
[00:20:33]
Like in my Elm GraphQL workshop that I do I talk about how like one of the exercises
[00:20:40]
I do is I have people try to send an empty selection set in GraphQL.
[00:20:46]
In raw GraphQL?
[00:20:47]
In raw GraphQL.
[00:20:48]
Okay.
[00:20:49]
Do you know what happens if you send an empty selection set?
[00:20:51]
It returns everything?
[00:20:52]
I hope not.
[00:20:53]
It's undefined, it's invalid GraphQL syntax but in Elm GraphQL if you do selection set.empty
[00:21:00]
it's valid and it does certain things under the hood and that can be useful in certain
[00:21:05]
ways to be able to define an empty selection set.
[00:21:08]
And I mean the point is that it's at a different level of abstraction and so one of my main
[00:21:13]
goals was not to port over GraphQL or some specific GraphQL client but was to make it
[00:21:20]
an Elm like experience and think about what I mean and I really started with what do I
[00:21:25]
want to make possible.
[00:21:27]
So I think that yeah that mindset like what do I want to make possible.
[00:21:31]
Richard had a really interesting quote in making possible states impossible.
[00:21:35]
He said a clearer data model can lead to a clearer API.
[00:21:40]
So let's talk a little bit about the process.
[00:21:42]
Like where do you start and like where do you start if you're designing application
[00:21:47]
code rather than library code?
[00:21:49]
Do you start with an API, a type, code, tests, like where do you start?
[00:21:55]
I'm not sure where I personally start.
[00:21:58]
I know that Richard advises starting with the model and I think that's what I do too.
[00:22:04]
I either start with the raw view with no model or I start with a model.
[00:22:09]
Yeah I think most cases I start with the model if I know that the model is going to be complex.
[00:22:15]
So I'm going to define everything that I know that I will need and yeah I'll try to think
[00:22:22]
about what data is related to another piece of data and group those together.
[00:22:29]
But yeah first try to make something work, something sensible and then hunt those impossible
[00:22:37]
cases.
[00:22:38]
Yeah I pretty often find myself just often I'll just start typing out a type and that's
[00:22:47]
a common prototyping technique that I use where I sketch out a type.
[00:22:50]
I start typing some types and I fit them together into other types and I think about what states
[00:22:57]
that makes possible or impossible and then the actual process of getting to that type
[00:23:03]
there might be a few steps in between there but maybe I model too many types.
[00:23:08]
Too many possible cases?
[00:23:10]
Maybe I model too many types for me to actually integrate into the code at once but I might
[00:23:16]
just sketch it out and then I have a roadmap of sort of what the types might look like
[00:23:21]
or what the pitfalls are like.
[00:23:23]
If I were to model it this way I would enable this possible type or I would run into this
[00:23:30]
problem.
[00:23:31]
So I often use that as a sketching technique and yeah but the whole question of incrementally
[00:23:37]
how do you move to types is also a really interesting one.
[00:23:39]
So one thing that I think is really interesting is like there's this idea of modeling impossible
[00:23:46]
states but one thing that I've found is that's really helpful that I think maybe Richard's
[00:23:53]
talk doesn't focus on that's sort of outside of the scope of Richard's talk is like what's
[00:23:58]
the process as you discover new impossible states?
[00:24:01]
How do you refine those types?
[00:24:03]
And I think that's a really important one because we don't know all the impossible states
[00:24:09]
when we initially start implementing the code.
[00:24:13]
Yeah likely you will discover what you will need to make impossible.
[00:24:16]
Maybe you don't even know what the end result for the product should be.
[00:24:22]
Exactly it's evolving.
[00:24:23]
You get new domain logic and rules that like I was working on some code recently regarding
[00:24:31]
steps in a workflow and you know the rules start to change around which steps can you
[00:24:36]
transition to, can you go backwards or forwards and these sorts of domain rules change but
[00:24:42]
over time as you discover these new constraints you build them in.
[00:24:49]
And another thing I find is really helpful is like if you find a bug stop and think can
[00:24:55]
I eliminate the possibility of representing this state?
[00:24:58]
So I think that's a huge insight for me that I think is really powerful just like when
[00:25:05]
you find a bug you're going and fixing the bug the first thing you should be thinking
[00:25:09]
is did it have to be representable?
[00:25:11]
Yeah can I make it so that the bug never appears again?
[00:25:16]
Exactly.
[00:25:17]
Instead of going out to write a test to where you can reproduce the error you go and change
[00:25:25]
a model or the types and make it impossible.
[00:25:30]
Yes exactly and actually often that process looks like you look at the type maybe you
[00:25:35]
play around with the type a little bit you know often I'll reproduce it before I start
[00:25:39]
thinking about what the implementation will look like in a test but then once you've got
[00:25:44]
it reproduced in the test you want to make sure like you said it never happens again
[00:25:49]
which means you want to make it impossible to represent.
[00:25:52]
And so that means that you may fix the test in a way where you're changing the type and
[00:26:00]
the test goes away you can no longer have the test but that's okay.
[00:26:04]
So Richard says if you make an impossible state impossible you can no longer write a
[00:26:11]
test for it and ever since I heard that like years ago I've been wondering but how do I
[00:26:16]
write a test to make sure that that impossible state doesn't happen again if I change my
[00:26:21]
model once more?
[00:26:23]
Right.
[00:26:24]
Like how do you make sure that that guarantee stays true?
[00:26:29]
And I still don't have an answer for that one.
[00:26:33]
I think one of the key points in this whole talk and way of thinking is ultimately it's
[00:26:41]
actually not so much about making things impossible but it's about having a single point where
[00:26:48]
something is defined.
[00:26:50]
So one way I think about this is like you can make a state impossible with conditionals.
[00:26:56]
You can have a bunch of if conditions and you can make it so the state will never happen
[00:27:00]
it's impossible.
[00:27:02]
Through your code right?
[00:27:03]
So like there's nothing inherently more possible or impossible when you make it impossible
[00:27:10]
through type or through code right?
[00:27:13]
So why is it important that we do it through types and not through code right?
[00:27:18]
Yeah and I'd say because you can know whether the thing is impossible just by looking at
[00:27:24]
the types.
[00:27:25]
Exactly.
[00:27:26]
That's that's the key right?
[00:27:27]
So it's not that it will never be possible again and you have guarantees around like
[00:27:33]
how it's going to evolve but it's like the fact that the type system is very simple is
[00:27:38]
important here because the more complex the type was if the type system had the ability
[00:27:44]
like and I think some type systems get very complex.
[00:27:48]
You know Scala I've heard has like some very nuanced things you can do with the type system
[00:27:54]
which is interesting.
[00:27:56]
I was thinking of Scala.
[00:27:58]
For library authors that's really cool because for library code you can put a lot of thought
[00:28:05]
into ensuring certain impossible states and have it in a certain community resource.
[00:28:13]
But for application code you want to be able to just look at the type and at a glance be
[00:28:16]
like oh no I don't even have to think about this.
[00:28:19]
This can never happen.
[00:28:20]
So it's just a way of reducing the cognitive load for understanding what your code might
[00:28:27]
do and it's a very powerful way of doing that because the Elm type system helps you do that
[00:28:32]
if you're doing case statements it's going to help you exhaustively check things.
[00:28:37]
The compiler will support you if you prove that you have a certain collection of data
[00:28:43]
and particular types then it will help you out with that knowledge that you've proven
[00:28:48]
to it.
[00:28:49]
So you get all these benefits but at the core you could prove that with just conditionals
[00:28:55]
and actual code that's going to execute not types that are going to type check through
[00:28:59]
the compiler.
[00:29:00]
And that's sort of the thing with the make data structures talk where he talks more about
[00:29:04]
opaque types.
[00:29:05]
We covered a lot of the points that I think are really interesting in our Elm radio episode
[00:29:11]
on that.
[00:29:12]
But so what it comes down to is it's actually not about making states impossible it's about
[00:29:18]
being able to see what's possible very quickly and have a single source of truth that's easy
[00:29:24]
to parse.
[00:29:25]
Now if you think about that in the context of opaque types why are opaque types so important?
[00:29:31]
And we talked about this in our opaque types episode.
[00:29:33]
It's because you can have a single gatekeeper that's responsible for checking those constraints
[00:29:41]
and enforcing them.
[00:29:43]
So Richard in make data structures talks about the word count of a chapter.
[00:29:49]
He's got like a data structure for a chapter and he was saying that he was getting race
[00:29:54]
conditions and bugs where that data was getting out of sync.
[00:29:58]
And suddenly when he made it an opaque type those bugs stopped happening.
[00:30:04]
And that is what's really powerful.
[00:30:06]
It's not that he's made anything impossible it's that he's made it so that he only has
[00:30:12]
to think about correctness in one small place and everywhere else he can assume it's correct.
[00:30:19]
And if there is an issue he knows exactly where to look.
[00:30:23]
Whereas if you have that at the wrong level of inversion right is that inversion of control
[00:30:27]
thing if you have the consuming code able to rely directly on the data types then you
[00:30:35]
have no way of enforcing those guarantees and you have to think about it at every call
[00:30:43]
site and it becomes the responsibility of all of the calling code everywhere in your
[00:30:46]
code base rather than a single source of truth.
[00:30:48]
So it's actually the exact same benefit that you get for a type except you're doing it
[00:30:53]
with code.
[00:30:54]
So the benefit of making impossible states impossible is that it shrinks the surface
[00:30:59]
area that you have to examine to understand correctness.
[00:31:03]
And opaque types do the exact same thing.
[00:31:05]
They shrink it by having a single point of truth for the logic and guarantees and everything
[00:31:10]
else can depend on that once you've verified that that point of truth is in good shape.
[00:31:15]
Yeah and ideally if you can you use both.
[00:31:19]
You make the type opaque so that you get all the guarantees outside of the module but inside
[00:31:25]
you still want to make impossible states impossible so that internally you know that you will
[00:31:30]
never get into this or that state.
[00:31:33]
That is a great point, yes.
[00:31:36]
It's not my point, it's Richard's.
[00:31:38]
Well I'm glad you brought it up.
[00:31:44]
It's really all about reducing cognitive load.
[00:31:47]
Yeah, maybe let's talk about when you should not try to make impossible states impossible.
[00:31:54]
Tell me more, I'm intrigued.
[00:31:56]
It's something I often see in the beginners or the general Slack channel is that people
[00:32:01]
try to make impossible states impossible in cases where it's very hard to make it impossible.
[00:32:10]
Like it becomes so cumbersome that you're not getting your money's worth by doing that.
[00:32:15]
Yeah exactly.
[00:32:16]
Like I think it often involves references like oh I have a list of IDs somewhere and
[00:32:26]
I need that ID to always exist in some other unrelated list and grouping them together
[00:32:34]
is just way too hard or you're not getting your money's worth of it.
[00:32:41]
And often I see people try to give solutions like you could try this, it's a bit annoying
[00:32:47]
but nah.
[00:32:49]
And I see other people saying well you shouldn't do it, you should just try to make it as good
[00:32:55]
as possible, try to make the easy impossible states impossible.
[00:33:00]
But the ones that are just too hard, just give up on them and instead cover those by
[00:33:06]
logic and by tests.
[00:33:08]
And usually you will be just fine with a few tests.
[00:33:12]
That's a great point.
[00:33:13]
Yeah I think it's about being pragmatic and you can get caught up in the theoretical.
[00:33:19]
It doesn't mean that you shouldn't think about it because maybe you can't think of one, of
[00:33:26]
a good solution and you ask around and people give you a good solution but sometimes it's
[00:33:31]
just way too hard.
[00:33:32]
Right and actually I noticed this in Richard's slides for make data structures.
[00:33:39]
He was talking about this technique where you have a document ID and he was exposing
[00:33:43]
an interface for getting a document ID and he was saying like you cannot create a document
[00:33:47]
ID from a string, that's just not allowed right?
[00:33:52]
Which is an amazing technique that's very powerful.
[00:33:55]
I think it's got a lot of great applications right?
[00:33:58]
But so instead of exposing like a from string where you create a document ID he was saying
[00:34:03]
well how can you get a document ID?
[00:34:06]
And the API clearly documents that.
[00:34:08]
You can get a document ID by decoding it or generating a random one.
[00:34:13]
Like those are the two ways you do it.
[00:34:15]
So the API that's exposed and the types of those exposed values clearly state how you
[00:34:21]
can get one.
[00:34:23]
Which is amazing right?
[00:34:25]
But well what if you took the decoder and you created a string and ran that decoder,
[00:34:32]
you could create a document ID from a string right?
[00:34:36]
And like sure you can.
[00:34:38]
It's not impossible and getting too wrapped up in this idea of like it needs to be impossible,
[00:34:44]
it's misguided, it's fine.
[00:34:47]
It's pretty clear how it's supposed to be used.
[00:34:49]
Is it going to be abused in that way?
[00:34:51]
Like it could be but at least it will be very obvious that somebody's doing something they
[00:34:56]
probably shouldn't be doing.
[00:34:58]
You can write an Elm review rule for that.
[00:35:01]
That's true.
[00:35:02]
If you see your co workers cheat and they don't want to follow your review tips then
[00:35:10]
yeah.
[00:35:11]
Yeah.
[00:35:12]
Write a rule for it.
[00:35:14]
Yeah I find that that's something that happens pretty often.
[00:35:19]
I mean we've talked about this with your phantom builder technique.
[00:35:26]
Which we haven't talked about.
[00:35:28]
Which we haven't done an episode on yet.
[00:35:30]
But that's another example where things can get sort of beyond pragmatic sometimes and
[00:35:37]
sometimes the right choice is to go with the simpler one to express because it's good enough
[00:35:42]
right?
[00:35:43]
If you have the Elm HTML API, can you have multiple ID tags in an HTML node?
[00:35:51]
Sure.
[00:35:52]
Should you make it harder to construct HTML elements to prevent that?
[00:36:00]
That's fine.
[00:36:01]
Don't worry about it.
[00:36:02]
It's not that big a deal.
[00:36:03]
It's probably not worth it.
[00:36:04]
As long as you make it more obvious that people are doing something wrong then yeah.
[00:36:09]
If you see HTML.ID with an empty string like, huh.
[00:36:15]
If you try to make an HTML node with a node type being an empty string again.
[00:36:21]
Exactly.
[00:36:22]
What are you doing?
[00:36:23]
And you could make the type of the ID attribute function that it takes a non empty string
[00:36:29]
right?
[00:36:30]
And there are all sorts of things you could do but it's okay.
[00:36:34]
You don't need to perfectly eliminate all states that you don't want.
[00:36:38]
It's pragmatic.
[00:36:39]
Be pragmatic about it.
[00:36:40]
Yeah.
[00:36:41]
Be pragmatic.
[00:36:42]
Just keep in mind the cost of it.
[00:36:43]
Because those cases feel a bit annoying to make it possible.
[00:36:50]
It makes it more cumbersome.
[00:36:52]
Too cumbersome maybe.
[00:36:54]
Because it still needs to stay a bit nice to work with.
[00:36:57]
But in some cases, yeah.
[00:37:00]
It's not that hard.
[00:37:02]
Just make it impossible.
[00:37:04]
It really depends.
[00:37:05]
Right.
[00:37:06]
It's one of these unfortunate cases that we see in life where there's no silver bullet.
[00:37:12]
It's not a one size fits all that you can just apply the same way every time.
[00:37:15]
It requires this thing called thinking and using your brain.
[00:37:19]
And it's unfortunate.
[00:37:20]
I know.
[00:37:21]
Yeah.
[00:37:22]
That's the worst.
[00:37:23]
Yeah.
[00:37:24]
Ultimately, you've just got to have this mindset of being aware of what states are you allowing
[00:37:31]
to be represented.
[00:37:32]
And then it requires experimentation and evolution and tweaking things with that mindset.
[00:37:40]
So we've got these built in types in Elm, like maybe and result.
[00:37:45]
When should you use those and when should you use a custom type?
[00:37:48]
In make data structures, Richard talks about the value of starting with your own custom
[00:37:55]
type that you've defined rather than these built in types.
[00:37:58]
But how do you decide which to use, which to start with?
[00:38:03]
I always start with the built in ones personally.
[00:38:07]
I guess I can allow myself to do that because I have some experience because I can sense
[00:38:12]
when something is off and I need to change it.
[00:38:15]
So he says start with a custom type.
[00:38:19]
A custom type, I mean.
[00:38:23]
And I think that could be a good solution for beginners because they will not rely too
[00:38:29]
heavily on maybes and booleans, which I think is good.
[00:38:35]
But I'm very happy with working with all the built ins when they fit.
[00:38:39]
So I will kind of use the technique of types as sets, like compute the cardinality of a
[00:38:45]
type and see if it matches exactly what I expect.
[00:38:49]
And if that works out, then I'm fine with the built ins.
[00:38:52]
Right.
[00:38:53]
Yeah.
[00:38:54]
I mean, I think it's important to keep in mind that there's definitely value to built
[00:38:58]
in types.
[00:38:59]
For example, if somebody is passing in a maybe, there are all these things.
[00:39:02]
Or if you're returning a maybe in your API, then you can do maybe dot map and you can
[00:39:08]
do maybe dot with default and you have all these nice APIs that come from the core APIs.
[00:39:16]
And result can be a very expressive type that makes it clear that something might fail.
[00:39:22]
So like you say, it takes some experience to know when to reach for one and when to
[00:39:27]
reach for a custom type.
[00:39:29]
I think that just practicing using some custom types is valuable.
[00:39:33]
And also Richard talks about how he said, well, I'll just start with using type aliases
[00:39:40]
for my document IDs.
[00:39:43]
Or, you know, he said, I'll get to that later instead of like going to the effort of creating
[00:39:50]
a module with an opaque type.
[00:39:52]
And then however many, you know, refactorings and bug fixes later, he's like, why didn't
[00:39:58]
I just start with, you know, writing an opaque type that defines the API for this thing in
[00:40:04]
a single place?
[00:40:06]
And then, sure, you have to define a way of accessing this value because you're not just
[00:40:12]
accessing the raw value directly.
[00:40:15]
You've wrapped it in an opaque type.
[00:40:17]
You have like a single variant custom type, for example, like he talks about in make data
[00:40:22]
structures.
[00:40:23]
He's like, yeah, like the cost of writing a handful of map and get functions is totally
[00:40:30]
worth what it would have saved me in like fixing all these bugs and impossible states.
[00:40:36]
But it feels annoying.
[00:40:37]
It feels annoying, exactly.
[00:40:40]
And that's like a habit that I think that's why it takes practice.
[00:40:44]
And I think it's worth getting out of your comfort zone and just try doing it.
[00:40:49]
If you go a little bit too far and you find later, you know, I went a little overboard
[00:40:53]
there, I can tone it down.
[00:40:54]
That's okay.
[00:40:55]
At least you'll get a sense of that.
[00:40:57]
But if you don't sort of push yourself a little outside your comfort zone, you won't learn
[00:41:00]
those skills.
[00:41:02]
So there's another interesting thing that came up in the make data structures talk,
[00:41:07]
which is sort of coupling to how exposing a public API and hiding the internals couples
[00:41:16]
you like effects coupling to the code.
[00:41:20]
So you know, he talks about like exposing an API for how you interact with the data
[00:41:25]
structures and how that allows you to change the internal implementation after the fact,
[00:41:31]
right?
[00:41:32]
Yeah.
[00:41:33]
So that's a really important consideration.
[00:41:36]
And also it's a helpful roadmap for moving something to an opaque type or to use a custom
[00:41:43]
type.
[00:41:44]
Yeah, that was pretty good.
[00:41:46]
Yeah.
[00:41:47]
So I think that's a good technique to keep in your back pocket to reach for often that
[00:41:53]
if you find that you have, you know, a type alias that maybe isn't representing possible
[00:41:59]
states well, or you know, maybe is or something like that, you know, well, you can start by
[00:42:05]
having a module that defines how to access them with a high level API that decouples
[00:42:10]
you from the concrete implementation and types.
[00:42:14]
And now that you've decoupled the API access from the concrete types and implementation,
[00:42:20]
now you can change the underlying types.
[00:42:22]
So I think that's a really handy technique for moving to better types and better data
[00:42:28]
modeling.
[00:42:29]
Yeah, you can do this bit by bit, you can do this in a few minutes, or you can do this
[00:42:33]
over a couple of months.
[00:42:35]
Yeah.
[00:42:36]
And that would both are fine.
[00:42:38]
Well, the faster you do it, the more benefits you'll see.
[00:42:42]
But yeah.
[00:42:43]
Yeah.
[00:42:44]
Have you heard people outside of the Elm community and outside of static type communities say
[00:42:51]
things like, call me when types can solve this particular business logic.
[00:42:56]
Yeah, I imagine as a coach, you often hear that.
[00:43:01]
Well, I mean, not from my clients, my clients are awesome.
[00:43:05]
And they care about modeling things nicely with types.
[00:43:10]
But yeah, I do hear that.
[00:43:13]
I think it's sort of an all or nothing thing that like, if it doesn't solve every problem,
[00:43:19]
then it's not worth anything.
[00:43:21]
Well, this type can't say whether my n isn't less than five.
[00:43:25]
This is useless.
[00:43:26]
Right.
[00:43:27]
Right.
[00:43:28]
And I think we talked about that in our opaque types episode that like, you can't guarantee
[00:43:34]
that no one will try to create that.
[00:43:36]
But you can guarantee that if they try to create it, you won't let them.
[00:43:42]
Yeah.
[00:43:43]
By conditionally returning a type by returning a maybe type that represents something in
[00:43:47]
the range zero to 50 or whatever.
[00:43:49]
And well, it says another way we can do that.
[00:43:52]
Right.
[00:43:53]
Using a type systems.
[00:43:57]
I think that making impossible states is a very good technique to let those people know
[00:44:02]
about.
[00:44:03]
Because there's so many is this logic errors that we don't have to handle that we can't
[00:44:08]
represent and once you get to know how to other types of some works and how to use it
[00:44:14]
well.
[00:44:15]
I agree with one caveat, which is, I wouldn't waste my time trying to convince those people
[00:44:20]
that that's a good idea.
[00:44:21]
They don't they don't want to try using type systems effectively.
[00:44:25]
I would save that conversation for somebody who cares who thinks that type systems are
[00:44:29]
an interesting idea.
[00:44:31]
And maybe they'll learn something interesting.
[00:44:33]
Well, if it's your boss, and you want to use the helmet work, then maybe the effort.
[00:44:39]
Yeah.
[00:44:40]
That's that may be that may be that's a caveat of the caveats.
[00:44:47]
So so you're in you and I, you and I had an interesting conversation offline.
[00:44:51]
And by offline, I mean on online about the domain of a test space, all the possible values
[00:44:59]
you could test.
[00:45:00]
So you know, I did a live stream recently with Corey Haynes about doing the gilded rose
[00:45:05]
cata, which is like a legacy code sort of simulates legacy code that you you first write
[00:45:12]
tests to simulate getting it getting legacy code under test, and then you start refactoring
[00:45:17]
it.
[00:45:18]
Yeah.
[00:45:19]
And beautiful code.
[00:45:20]
It's beautiful code, lots of nested conditionals.
[00:45:24]
And so, you know, we were using snapshot testing to get that under test.
[00:45:29]
And you and I were talking about how could you get the whole domain under test?
[00:45:34]
And how could you guarantee that?
[00:45:36]
How could you guarantee that your snapshot tests are covering all of the behavior?
[00:45:40]
Yeah, because when you the first step of what you were trying to do is get 100% code coverage,
[00:45:47]
which you got, right?
[00:45:49]
Even it's not possible.
[00:45:51]
But in this in this example, it was Yeah.
[00:45:54]
And when you say 100% code coverage, I want to clarify, like, the interesting thing is
[00:45:58]
not lines.
[00:45:59]
It's like, if you change any of the behavior, you're guaranteed that a test will catch that
[00:46:06]
bug.
[00:46:07]
Yeah, which you won't get with code coverage.
[00:46:10]
That's right.
[00:46:11]
Yeah.
[00:46:12]
But so the goal is that 100% code coverage, it's Yeah, yeah, you did get 100% good coverage,
[00:46:17]
but it still doesn't give you the guarantee that you got the whole domain logic covered.
[00:46:23]
Right.
[00:46:24]
And that was the point of our conversation.
[00:46:27]
Right.
[00:46:28]
And so we, we got to this interesting conclusion, which is that if you have, you know, the if
[00:46:36]
you think of the domain as like, all the possible inputs to the code under test, right?
[00:46:42]
If the size of the domain, if the cardinality of the domain is infinite, right, then you
[00:46:47]
can't prove it.
[00:46:48]
But if the cardinality of the domain is finite, and of a small enough size, which tech, technically,
[00:46:54]
the cardinality of all data types in a computer is finite, technically, right, but go test
[00:47:02]
them all.
[00:47:03]
Yeah, go test them all.
[00:47:05]
But we could still think of think of them conceptually as infinite.
[00:47:08]
But if you constrain your data types, you can actually reduce the cardinality.
[00:47:13]
So if we have so for example, you know, in the gilded rose cata, if the possible domain
[00:47:19]
of the cell in date is like between zero to 50 days, well, if we define a custom type,
[00:47:30]
that expresses that constraint that it can be between zero to 50 days, it's no longer
[00:47:35]
an infinite type.
[00:47:36]
The cardinality is now 51 or whatever, right?
[00:47:39]
So which can be manageable, right?
[00:47:41]
That that's something that we can exhaustively test.
[00:47:44]
So if now, how would you represent that?
[00:47:47]
Well, you would need to use an opaque type.
[00:47:50]
And you'd need to conditionally return that opaque type so that if you don't return it,
[00:47:54]
you only return it if it meets that condition.
[00:47:56]
But you could you could do that.
[00:47:58]
And using that you could exhaustively check all possible inputs in the domain.
[00:48:05]
So that's a really interesting thing.
[00:48:07]
Now, you know, that's a sort of like pushing the boundaries of some theoretical thing.
[00:48:11]
But more pragmatically, and cata's are a nice way to explore pushing those boundaries, right?
[00:48:17]
But more pragmatically in your code, you can you can think in the same way that it reduces
[00:48:23]
the number of areas you have to think about and the number of areas you have to test,
[00:48:27]
you can get more complete coverage and confidence about things and, and potentially if if the
[00:48:32]
cardinality of your, you know, input domain is 100, then you can exhaustively capture
[00:48:39]
the behavior of all 100 of those test cases, you can refactor and change the structure
[00:48:43]
at will, as long as your test pass, which is kind of cool.
[00:48:47]
Yeah, because you covered everything.
[00:48:49]
Yeah.
[00:48:50]
Mm hmm.
[00:48:51]
So how would somebody get started trying to apply this technique trying to get better
[00:48:56]
at these skills?
[00:48:57]
Well, first of all, watch Richard's talks.
[00:49:02]
You haven't already.
[00:49:03]
Oh, it's so good.
[00:49:04]
So again, it's making a put.
[00:49:06]
How many times have you watched them?
[00:49:08]
You're in less than 10.
[00:49:12]
Nice is what I'm going to answer.
[00:49:15]
It's really fun to watch with other people too.
[00:49:18]
Yeah, I've never had the chance to talk to see it with anyone else.
[00:49:23]
But mm hmm.
[00:49:24]
But yeah, less than 10 each.
[00:49:26]
Yeah.
[00:49:27]
Nice.
[00:49:28]
So yeah, go watch those talks.
[00:49:31]
So they're making possible states impossible from Elm Europe 2018.
[00:49:37]
And make data structure from Elm Europe 2018 and make impossible says possible from Elm
[00:49:42]
comp 2016.
[00:49:44]
We're going to release this right.
[00:49:46]
So the time that you're hearing this, it is the four year anniversary or at least the
[00:49:50]
same week of the making possible states impossible talk.
[00:49:55]
Yeah.
[00:49:56]
It feels like it's been around forever.
[00:49:58]
I know.
[00:49:59]
Yeah.
[00:50:00]
If I have to specify one mantra about Elm, like, yeah, hey, what is one of the core concept
[00:50:06]
of Elm?
[00:50:07]
It's making impossible states possible.
[00:50:09]
But it's absolutely not related to Elm at all.
[00:50:12]
The idea is called making make illegal states unrepresented, unrepresented.
[00:50:20]
And other languages like Haskell or, well, I guess, any static type language.
[00:50:28]
I know they've put that idea to reason also, but I guess so many others.
[00:50:33]
But Richard just made us discover that in a very good way.
[00:50:38]
Yeah.
[00:50:39]
Have you seen Evan's tweet from a few years back that lays out like the Elm philosophy?
[00:50:47]
Yeah.
[00:50:48]
Yeah.
[00:50:49]
So one of the ones in there is making possible states impossible.
[00:50:53]
So like a few highlights, understand the problem.
[00:50:57]
I think that really ties in.
[00:50:58]
Overrated.
[00:50:59]
Explore all possible solutions.
[00:51:04]
These are also very intertwined with this idea of API design, make impossible states
[00:51:09]
impossible.
[00:51:10]
You can't separate these ideas.
[00:51:11]
Explore all possible solutions.
[00:51:13]
Pick one.
[00:51:14]
I like that one too.
[00:51:16]
Simplicity is not just for beginners.
[00:51:18]
It's better to do it right than to do it right now.
[00:51:20]
It's not done until the docs are great.
[00:51:22]
Take responsibility for user experiences.
[00:51:24]
That's one that you talk about often, Jeroen.
[00:51:27]
So these things all go together.
[00:51:30]
It's just a way of thinking and a mindset and push your limits a little bit with how
[00:51:34]
you're applying these techniques.
[00:51:36]
It's going to be a little bit uncomfortable at first, but you need to do that to get those
[00:51:40]
light bulbs and to gain that experience and develop those skills.
[00:51:44]
Yeah.
[00:51:45]
And at some point you will see the benefits.
[00:51:47]
You might not see them right away.
[00:51:50]
At some point you'll see them.
[00:51:52]
I remember one time where one of my previous jobs where I used Elm.
[00:51:58]
We had a routing system.
[00:52:00]
So we had a route type and we just had a custom type for every page.
[00:52:06]
And I remember that we needed to add analytics for every route.
[00:52:10]
So for every constructor you mapped, one analytics value.
[00:52:16]
I left on holidays and when I came back, I was like, oh my God, I forgot to tell them
[00:52:21]
to do that because I designed that system, but I totally forgot to tell my coworkers
[00:52:26]
to not forget to do the analytics.
[00:52:30]
And well, because the Elm compiler had their back, they did it.
[00:52:34]
Oh wow.
[00:52:35]
That's so cool.
[00:52:36]
Yes.
[00:52:37]
I didn't have to tell them like, okay, well it's there actually.
[00:52:40]
I love that.
[00:52:41]
Why did I worry?
[00:52:42]
That's amazing.
[00:52:43]
And that was just a simple custom type, like nothing complicated about it.
[00:52:48]
Right.
[00:52:49]
I think about that often just like when somebody goes to build the next feature or change this
[00:52:54]
thing, what's the compiler going to tell them?
[00:52:56]
How is the compiler going to help them do that?
[00:52:59]
It's not just for you right now.
[00:53:01]
It's for you later and for other people now and later.
[00:53:05]
Exactly.
[00:53:06]
Yeah.
[00:53:07]
I think another thing getting started on this.
[00:53:11]
Again, you can create a high level API to decouple from the implementation before you
[00:53:17]
actually go and change the underlying implementation.
[00:53:20]
So that gives you a space to experiment without breaking the consuming code.
[00:53:26]
But another helpful thing is to look out for certain smells.
[00:53:30]
So if you have things that are being validated, could you make it impossible in the first
[00:53:36]
place?
[00:53:37]
If you're returning results, sometimes you have to return a result, it's user input or
[00:53:42]
you can't know things.
[00:53:43]
But think about is it possible to eliminate the possibility of failure by the guarantees
[00:53:48]
I can provide with my types and my API?
[00:53:51]
Yeah.
[00:53:52]
You also have conditionally doing things based on two fields or two pieces of data.
[00:53:57]
So if you do maybe this is just and this is just, then maybe that should have been one
[00:54:05]
data, one type.
[00:54:07]
Right.
[00:54:08]
Because that gives you more fine grained control about the relationship.
[00:54:12]
If one of them is present, does that mean the other one has to be?
[00:54:16]
Yeah.
[00:54:17]
To model that better.
[00:54:18]
So yeah, look at yeah, look for maybes and consider using custom types to give you more
[00:54:22]
fine grained control over the combinations that are possible.
[00:54:27]
Don't use Boolean's too much.
[00:54:29]
Boolean blindness.
[00:54:30]
Mm hmm.
[00:54:31]
Mm hmm.
[00:54:32]
Yeah.
[00:54:33]
Look for type aliases too.
[00:54:34]
You know, Richard showed his example where he's like, oh, great, I can have a nice self
[00:54:39]
documenting type where I just use a type alias, but it's actually a string under the hood.
[00:54:43]
But of course, you know, that's we know what kinds of problems that can lead to.
[00:54:47]
Richard talks about that and make data structures.
[00:54:50]
So that's another potential smell.
[00:54:52]
Also modules.
[00:54:54]
I've actually thought about this could be an interesting Elm review rule to write.
[00:54:58]
If you have types defined in one module, and then a bunch of functions for interacting
[00:55:04]
with that type in a different module that directly depend on the concrete like constructor
[00:55:11]
variants of that type.
[00:55:13]
That's another code smell, right?
[00:55:14]
Having like a types dot Elm module or a model dot Elm module.
[00:55:18]
Yeah, yeah.
[00:55:19]
Don't do that.
[00:55:20]
Yeah.
[00:55:21]
So those are those are things that can help give you a starting point for looking for
[00:55:24]
these opportunities.
[00:55:25]
Yeah.
[00:55:26]
Well, I think we covered a lot.
[00:55:28]
Yeah.
[00:55:29]
And if we forgot something, go watch Richard's talks.
[00:55:33]
Yes.
[00:55:35]
And yeah, go play experiment for yourself and have fun.
[00:55:40]
And if only we could make this state impossible, Jeroen.
[00:55:43]
If you if you love the podcast, and you haven't rated us on Apple podcasts, love to make that
[00:55:50]
state impossible.
[00:55:51]
But unfortunately, we're just gonna have to politely ask people to do it.
[00:55:54]
Yeah.
[00:55:55]
It's impossible because they have to watch it first and then they like it.
[00:55:58]
So there's a state where they have not liked it, but have watched it and listen to it.
[00:56:04]
That's right.
[00:56:05]
It's a very, very nuanced state to model.
[00:56:07]
Yeah.
[00:56:08]
We have to live with it.
[00:56:10]
And if you have a question, and you haven't asked it, go to Elm dash radio dot com slash
[00:56:16]
question ask us question.
[00:56:18]
Follow us on Twitter.
[00:56:19]
And thanks for listening.
[00:56:20]
You're in.
[00:56:21]
Have a great day.
[00:56:22]
See you next time.