spotifyovercastrssapple-podcasts

Codecs

Codecs do two-way Encode/Decode. We talk about this pattern in Elm, and some codec tools for JSON and Bytes.
November 16, 2020
#17

Backwards compatibility

Keeping data in sync

Elm codec API - string, Bool, object

Transcript

[00:00:00]
Hello, Jeroen.
[00:00:01]
Hello, Don.
[00:00:02]
How are you doing today?
[00:00:03]
I'm doing pretty well.
[00:00:04]
How about you?
[00:00:05]
I'm good.
[00:00:06]
I'm good.
[00:00:07]
I am excited to dive into the topic today.
[00:00:12]
Yeah.
[00:00:13]
Today we're talking about codecs, which is something we don't talk about quite often,
[00:00:17]
I think.
[00:00:18]
Yeah, it seems to be an emerging pattern that you see popping up more and more.
[00:00:23]
I think it's one of those little community patterns that evolves over time and there's
[00:00:29]
this sort of seed of an idea that sprouts all over in the community.
[00:00:34]
It seems to be one of these things.
[00:00:36]
Yeah.
[00:00:37]
So, maybe let's describe what a codec is.
[00:00:40]
Yes.
[00:00:41]
So, a codec is a short word for codec...
[00:00:44]
Man, I don't know, actually.
[00:00:47]
Let's look it up.
[00:00:48]
I think it's for encoder and decoder, or decoder and encoder.
[00:00:52]
I don't know which way it is, but it's a contraction of both.
[00:00:55]
Code decode.
[00:00:56]
So, it's a utility or a tool or a library that allows you to encode something from A
[00:01:02]
to B and decode it from B to A.
[00:01:05]
Oh, I've got a trivia fact for you, Jeroen.
[00:01:08]
So, a codec...
[00:01:10]
Is a portmanteau?
[00:01:11]
A portmanteau of coder, decoder.
[00:01:14]
You also looked at the Wikipedia page.
[00:01:16]
I'm looking at the Wikipedia page as well.
[00:01:19]
So, it's kind of cool because that is the definition that it is something that decodes
[00:01:24]
and encodes.
[00:01:25]
So, that's a very good choice of word, which I think maybe Leonardo, Mini Bill, was the
[00:01:31]
first one to come up with that term, I assume.
[00:01:33]
He created the Elm codec library for JSON encoding and decoding simultaneously.
[00:01:38]
So that's sort of the origin, I think, of that term in the Elm community.
[00:01:42]
In the Elm community, probably.
[00:01:45]
Maybe someone did something for older versions of Elm.
[00:01:50]
As far as Elm 0.19, I think that's the earliest one I know, at least.
[00:01:57]
Yeah.
[00:01:58]
So, that's essentially the...
[00:02:00]
That's the concept, is something that both encodes and decodes.
[00:02:05]
And so, that is a...
[00:02:07]
There is an Elm codec library, but there are actually now multiple libraries emerging for
[00:02:12]
things like dealing with bytes, for example.
[00:02:16]
And I think it's worth noting that this is both a pattern and a set of...
[00:02:23]
A library and a set of libraries, but it's also a pattern.
[00:02:26]
Yeah.
[00:02:27]
It's more of a pattern.
[00:02:28]
Yeah.
[00:02:29]
It's more a pattern than anything.
[00:02:30]
And I think that...
[00:02:31]
One of the things that I think is really interesting is...
[00:02:34]
So there's this sort of reversible nature, right?
[00:02:37]
When you're...
[00:02:38]
This idea that you're building up an encoder and a decoder at the same time and that it's
[00:02:43]
reversible.
[00:02:44]
And that I think would be sort of the textbook definition of this concept of a codec.
[00:02:48]
But then there's another concept here, which I think is really interesting and valuable
[00:02:53]
in the Elm community, which is the idea that you build up as much information as you can
[00:02:59]
at once because you sort of...
[00:03:01]
You can lose that information otherwise.
[00:03:02]
So if you build up a decoder and an encoder, right, then they're two separate things.
[00:03:10]
And you have to... maybe you have some fuzz tests to make sure that they're reversible
[00:03:14]
or you have to do a little extra work to keep them in sync and make sure they are reversible.
[00:03:21]
Yeah.
[00:03:22]
Making sure that when you encode something and then decode it, you always get the same
[00:03:25]
thing back and the same thing for decoding than encoding.
[00:03:30]
Yes.
[00:03:31]
Right.
[00:03:32]
Right.
[00:03:33]
And so that's like...
[00:03:34]
We can do better than just trying hard to do that and then making sure we did a good
[00:03:39]
job after the fact through...
[00:03:41]
If we automate it with some tests and stuff, that's great.
[00:03:44]
But what if we could build it up in such a way where we keep things in lockstep through
[00:03:50]
the API we use to build it up?
[00:03:52]
So I don't know if that specific concept is...
[00:03:56]
Maybe it's part of a codec, but also a pattern that comes up outside of this idea of codecs.
[00:04:04]
Like for example, in Elm GraphQL, that's an essential concept as well, that you are building
[00:04:10]
up the information you need to perform a GraphQL query as well as the information you need
[00:04:17]
to decode that.
[00:04:19]
And in Elm, these pipelines that you use to build up, whether it's just a vanilla decoder
[00:04:26]
and Elm GraphQL selection set, there are all these different data types, like a validation
[00:04:32]
sort of API for validating form input or whatever.
[00:04:39]
You're building up data in a way where the Elm type system keeps track of the data as
[00:04:43]
you build it up, but you're not only building up that data, you can transform that data
[00:04:49]
along the way, and you're tracking this underlying information about that data.
[00:04:57]
And so there's something there that maybe that's a separable principle that you can
[00:05:02]
use outside of this notion of a codec, which is like a reversible encoder, decoder.
[00:05:09]
So where would you use this as a very simple instance?
[00:05:13]
Would you use this, for instance, when you deal with HTTP requests?
[00:05:17]
What is the smallest thing where you would apply codecs?
[00:05:22]
It's a good question.
[00:05:23]
I think that probably one of the most common use cases for a codec is going to be data
[00:05:31]
that you control, because if it's data that you don't control, it's coming back from a
[00:05:37]
server that you don't control, then there might be a disparity between the data you're
[00:05:42]
decoding and the data you're encoding.
[00:05:45]
But if you control the data, for example, if it's user settings and you want to be able
[00:05:52]
to serialize it and deserialize it, if you want to update the user settings, which you
[00:05:57]
have in some Elm data type, and then you want to serialize that, put it into local storage,
[00:06:04]
and then read it back out, that's a perfect example that you just want this reversible
[00:06:08]
thing.
[00:06:10]
You control the serialization and deserialization, and you control both of those things in your
[00:06:15]
Elm code.
[00:06:16]
Yeah, I imagine that you would have multiple decoders built in.
[00:06:22]
For instance, you want to save your user settings.
[00:06:26]
Those might change over time.
[00:06:28]
At first, you only save A, and then later on, you add B. So your first codec would only
[00:06:36]
encode A and decode A, but your second decoder later on would also encode B and decode B,
[00:06:45]
but it would still need to work in the version where B does not exist.
[00:06:51]
Yeah, making it backwards compatible and nonbreaking.
[00:06:54]
Yes.
[00:06:55]
That's a shorter way of saying it.
[00:06:58]
Yeah, that's definitely a whole topic on its own.
[00:07:04]
And it is interesting how codecs fit into that picture because they are compatible with
[00:07:09]
it, but they don't in themselves enforce that concept, but they can help with that.
[00:07:14]
Yeah.
[00:07:15]
That's a good point.
[00:07:16]
I don't think I've seen one implementation of that where a backwards compatibility would
[00:07:22]
necessarily work.
[00:07:24]
I don't know.
[00:07:25]
They do say things about that in their frequently asked questions.
[00:07:30]
Yeah.
[00:07:31]
And Mario Rogic has this idea he calls Evergreen that he gave a really great Elm Europe talk
[00:07:37]
about that one year.
[00:07:39]
And he has that actually working in practice in Lambda.
[00:07:45]
So this idea of managing breaking changes is something that exists out in the wild.
[00:07:51]
And you could certainly apply that concept to codecs.
[00:07:55]
And I think under the hood, I believe Lambda may even be using one of these codec libraries
[00:08:02]
under the hood.
[00:08:03]
I know it's serializing and deserializing these things as bytes.
[00:08:06]
Yeah.
[00:08:07]
I think I've seen that there are limitations in what you can do with your migrations in
[00:08:13]
Lambda because of that.
[00:08:14]
Because it's trying to stay backwards compatible.
[00:08:20]
When you succeed, then that's just an amazing experience for your user.
[00:08:25]
So yeah, I agree with the way you can control the data.
[00:08:31]
As we said, when you don't control the data as much as you want only in Elm, then you're
[00:08:37]
dependent on what it looks like from the server or from some public API.
[00:08:42]
Right.
[00:08:43]
Yeah.
[00:08:44]
And then it will probably look like something that is a copy of the API response.
[00:08:51]
What I mean is you don't want to parse the API endpoint and put it into a model just
[00:08:57]
like you want to model it so that you don't have impossible states.
[00:09:03]
Although you can capture that in a codec where you can build up custom types.
[00:09:10]
And then of course, it's reversible so you build up a custom, you can encode a custom
[00:09:17]
type and decode it as well.
[00:09:20]
And if you have something like, if you use some of these serverless sort of Elm, things
[00:09:27]
that run like Elm code for serverless functions and return JSON data and stuff, right?
[00:09:33]
If you do code sharing, then you can use a tool like that to ensure that these two data
[00:09:41]
types are in sync.
[00:09:42]
Yeah.
[00:09:43]
Which, of course, you do run into these tricky questions about backwards compatibility there
[00:09:47]
as well because...
[00:09:48]
Yeah, and synchronization.
[00:09:49]
Yes.
[00:09:50]
Yeah, just like we talked about in the GraphQL episode last time.
[00:09:55]
Exactly.
[00:09:56]
Yeah, those same sort of concepts.
[00:09:58]
What's that called, the Ottoman general problem or I don't know, some sort of general problem,
[00:10:04]
which is this sort of conceptual problem in computer science that if you have a general
[00:10:12]
who needs to send a message to the troops that they're going to attack, but they will
[00:10:18]
only attack if he needs to send the message to the troops.
[00:10:25]
Then the troops need to send a confirmation that they received the message to attack because
[00:10:30]
both sides of this hill need to attack simultaneously.
[00:10:34]
And if they don't both have a synchronized plan to attack at the same time, then they
[00:10:40]
need to call off the attack.
[00:10:41]
So they can only do it if they get confirmation.
[00:10:43]
So the one side, side A needs confirmation that side B, but then side B needs confirmation
[00:10:51]
that side A received their confirmation.
[00:10:54]
There's no resolution.
[00:10:56]
It's a mess.
[00:10:57]
There's no way to solve that problem.
[00:10:58]
So there are certain conceptual limits to these things, but we kind of discussed that
[00:11:04]
in the Elm GraphQL episode that there are ways to get around that.
[00:11:08]
But yeah, those are some tricky problems.
[00:11:10]
But anywhere that you're able to sort of control the serialization format in your Elm code,
[00:11:17]
whether that's Elm running on a serverless function and then being serialized in a serverless
[00:11:24]
function run by Elm, and then a shared code base that deserializes it on the client side
[00:11:31]
in a browser in Elm, that same concept applies that you control the format in a shared Elm
[00:11:37]
code base.
[00:11:38]
So should we get into the more sort of detailed API of what you can do with Minibil Elm codec?
[00:11:47]
Yeah.
[00:11:48]
So like, I mean, first of all, the simplest thing you could imagine doing is just doing
[00:11:55]
a codec for a string, right?
[00:11:59]
So if you just do codec.string, now you have something that knows I can get a decoder for
[00:12:07]
this, which is json.decode.string under the hood, and I can get an encoder from this,
[00:12:12]
which is encode.string.
[00:12:14]
So it keeps track of that information.
[00:12:17]
And you got one for every basic type.
[00:12:20]
You got audience, you got floats, ints, characters.
[00:12:23]
Yeah.
[00:12:24]
So if it was as simple as, you know, if you were only dealing with built in Elm types,
[00:12:31]
you know, string, bool, int, float, and then maybes and lists of those, or even dicts and
[00:12:37]
sets and tuples, if that was all you were doing, then you wouldn't have to do very much
[00:12:43]
work.
[00:12:44]
You pretty much get all of that for free.
[00:12:45]
You just say this is a list of strings.
[00:12:49]
This is a list of ints or whatever, and it's just going to work.
[00:12:53]
There's no extra effort for you to describe how to serialize and deserialize because it
[00:12:59]
knows how to encode it into a list of strings, and it knows how to decode that into a list
[00:13:05]
of strings.
[00:13:06]
But once you get into things like objects, now that becomes a little more intricate because
[00:13:13]
now if you're building an object, if you're turning that object into a record on the decode
[00:13:21]
side, on the Elm side, then you need to then tell it how to pick apart that field from
[00:13:29]
a record, or it may not be a record.
[00:13:31]
It may be some other data type, but you need to tell it how to grab that field so it can
[00:13:37]
encode it when it's encoding that value.
[00:13:40]
Yeah, and it doesn't really have to look just the same on Elm and JavaScript.
[00:13:46]
So you can have a record on the Elm side and have an array on the JavaScript side if you
[00:13:53]
try to optimize it one way or another.
[00:13:55]
You could do it like that if you wanted to.
[00:13:59]
Can you do that with the Elm codec?
[00:14:00]
You mean using codec.object?
[00:14:02]
Yeah, because your codec.object takes a constructor just like decode.succeed.
[00:14:12]
Which would typically be a record constructor that people would pass in there, but it can
[00:14:17]
be any function that takes those data types.
[00:14:21]
And then you use the builder pattern where you say codec.field and then the name of the
[00:14:29]
field in JavaScript.
[00:14:31]
So in this case, you're saying, I tried to get this field with this name.
[00:14:38]
So you're saying you could turn it into a list on the Elm side, but then when it turns
[00:14:45]
it into a JavaScript object, codec.field is always going to turn it into a JavaScript
[00:14:51]
object on the encode side.
[00:14:53]
Right.
[00:14:54]
Yeah, it does look like this API does not allow you to do this, what I said.
[00:14:59]
You could imagine that you encode it differently in JavaScript.
[00:15:04]
That's right.
[00:15:05]
Have you used Elm serialize?
[00:15:07]
Did you try using that?
[00:15:08]
Yeah.
[00:15:09]
Okay, so you want to give a brief intro to that library?
[00:15:13]
Yeah, so I used Martin Stewart's Elm serialize, which was previously called Elm codec bytes,
[00:15:21]
if I remember correctly.
[00:15:22]
Now it's called Elm serialize.
[00:15:25]
So it's basically the same thing as Elm codec, but it doesn't work with JSON.
[00:15:28]
It works with bytes or strings because you can convert it to the ideas that you generate
[00:15:34]
more performance, JSON representation or bytes.
[00:15:38]
Right, because you don't care about the underlying type that's used.
[00:15:44]
You just care that you can take a data type in Elm, deserialize it to some format, which
[00:15:51]
you don't care what that format is, as long as you can also get it back using that codec
[00:15:57]
that you build up.
[00:15:58]
So the use case is like you only want yourself or this code to be able to read it.
[00:16:04]
You don't care about the rest.
[00:16:06]
And when you're in that use case, you can optimize a lot of it.
[00:16:11]
So in this case, you do turn things into like an array with zeros instead of custom type
[00:16:18]
names.
[00:16:19]
And it's a very, very compact JSON object that you get or bytes.
[00:16:24]
So I use it in Elm review to cache the internal ESCs for files.
[00:16:30]
That saves a lot of disk space.
[00:16:31]
It takes like, I think it saves like 60% of disk space compared to the Elm syntax, original
[00:16:40]
decoder and encoder.
[00:16:43]
Which is like JSON.
[00:16:44]
Which is JSON and with long fields.
[00:16:46]
So that one is meant to be human readable.
[00:16:49]
What I use isn't.
[00:16:50]
It's only meant for my internal cache.
[00:16:53]
So yeah, you can use codecs for caching, which has been my only use case for now.
[00:17:01]
For Elm codec, you could imagine that it's not codec.field, but you could have codec
[00:17:05]
index at and do something else where you would have decoder that decodes one way and encoder
[00:17:14]
that encodes one way.
[00:17:16]
Right.
[00:17:17]
Right.
[00:17:18]
Yeah.
[00:17:19]
And so that's nice to be able to build up an object.
[00:17:24]
And it's this underlying principle again, that at the point that you have that information,
[00:17:31]
you're basically giving it all the information it needs for one step.
[00:17:35]
So you could build up a decoder and then you could build up an encoder.
[00:17:40]
But to do that, you now have to duplicate all this information about it.
[00:17:47]
When you say encode.string one place, and then you have to say decode.string in the
[00:17:52]
other place.
[00:17:53]
Whereas when you build up a codec, you don't need to do that because you have that information
[00:18:00]
in a codec.
[00:18:01]
And we've talked about making possible states impossible in terms of data modeling.
[00:18:06]
We've also talked about the role of API design in making impossible states impossible.
[00:18:12]
And I think this is one of those instances where maybe this is like another special case
[00:18:17]
of that where through the API design, you capture all the information when you have
[00:18:24]
it so you don't have duplicate sources of truth.
[00:18:27]
You have one source of truth and you take all that information.
[00:18:31]
So that's what a codec is doing.
[00:18:32]
Instead of building up the encoder and the decoder separately, we have to duplicate all
[00:18:36]
this information.
[00:18:37]
You build them up together.
[00:18:39]
And there are certain things that you cannot make mistakes on.
[00:18:43]
And then there are places where you can make a mistake.
[00:18:46]
So you build up a codec.
[00:18:49]
If you build up a codec for an object, you do codec.object and then you give it a record
[00:18:54]
constructor.
[00:18:56]
So the example in the docs is a point with a record that's got an x and y float.
[00:19:03]
So then you do codec.object, you give it the point record constructor, and then you pipe
[00:19:08]
that into codec.field with the string x, which is the field name in JavaScript.
[00:19:13]
Now if you gave that something like empty string, you could mess that up, but you're
[00:19:18]
going to have to do something like that.
[00:19:20]
So that's not anything new that you could mess up compared to.
[00:19:24]
Jason decoding or Jason encoding.
[00:19:27]
Exactly.
[00:19:28]
And then you need to give it the second argument you give to codec.field.
[00:19:32]
In the example he gives.x, which would be a function that takes that record type and
[00:19:39]
pulls off the value for the encoder.
[00:19:42]
From the Elm code.
[00:19:43]
Yeah, from the Elm record.
[00:19:44]
Yeah, right.
[00:19:45]
It takes the value from the Elm record.
[00:19:47]
So it could be any function which takes whatever value you're dealing with in this point, it's
[00:19:52]
a point record, and then gets some sort of float data type back.
[00:19:58]
And of course you could put.y where you meant.x and you can make mistakes there.
[00:20:04]
And then you say codec.float as the final argument that says this is how you serialize
[00:20:10]
and deserialize it.
[00:20:11]
That part is guaranteed to be in sync.
[00:20:15]
You can't mess that part up.
[00:20:16]
So there are just more guardrails to things being in sync, and there is some manual work,
[00:20:23]
but at least you're capturing the information while you have it.
[00:20:27]
And I think that's the underlying concept that I really like.
[00:20:30]
And I think that that concept is applicable in more places than just this pattern.
[00:20:34]
Yeah, totally.
[00:20:35]
So as a recap, field takes as a first argument where in the JavaScript you need to write
[00:20:43]
or read the data from.
[00:20:46]
You got the second argument that is where do you get the value from the record from
[00:20:51]
in Elm.
[00:20:52]
And the third argument is what is the type of that field.
[00:20:57]
And because you have both reading and decoding and encoding at the same place, it's much
[00:21:05]
less likely that you're going to make a mistake.
[00:21:07]
That's another good point.
[00:21:08]
Right.
[00:21:09]
Because you can, if you see, if you give a JavaScript object field name x and then you
[00:21:16]
give a record accessor function.y, then you can look right next to each other and you
[00:21:22]
can see visually it's more obvious that something's gone wrong.
[00:21:26]
So yes, not only are you not losing that information as you build it up in two separate places,
[00:21:33]
but you can see the information together.
[00:21:36]
So if there's a mistake, it's easier to catch.
[00:21:39]
And you will not be able to leave out of a field, for instance, or as easily at least
[00:21:46]
because you can still do that in the object constructor or the record constructor.
[00:21:54]
Since you want the encode format and the decode format to always be in sync, you really want
[00:21:59]
to have all the guardrails available to make sure that you don't mess those up.
[00:22:05]
And when you have an encode that is very far away from decode, like maybe just a few lines
[00:22:10]
away, but that might still be too far away, then you're more likely to mess up.
[00:22:17]
And this is my main reasoning for using something like a codec is when the decoding and encoding
[00:22:24]
are the same or represent the same thing, you want to have all the tools available.
[00:22:31]
Should we talk about how you decode custom types?
[00:22:34]
That's one of the really clever design elements in this library is that solution to creating
[00:22:41]
a codec for a custom type.
[00:22:43]
So essentially, there's a little bit of a learning curve to figuring out how to work
[00:22:49]
with this, but you're building up this codec.custom, which it takes a function, and that function
[00:22:57]
is going to take an argument for every single variant that you have.
[00:23:03]
So if you have a teacher and student variant for a person, then so you'd have an argument
[00:23:11]
for teacher, an argument for student, and an argument for the actual value that you
[00:23:18]
received.
[00:23:19]
So you can do a case statement on that value on that person, and then say if it's a teacher,
[00:23:26]
then you destructure that teacher, and then you have to call that argument that you had
[00:23:31]
for teacher, and that argument is the encoder being passed in.
[00:23:36]
Yeah, it's so weird.
[00:23:39]
That's the thing that hurts your brain the most, and that's the really clever thing about
[00:23:43]
this design is it finds a solution to sort of capturing that information, but it definitely
[00:23:51]
hurts your brain at first.
[00:23:52]
So if you think about it, what do you need to teach it in order to have it reversibly
[00:24:00]
decode and encode a custom type?
[00:24:04]
And what you need to teach it is, well, how do I pick apart the information from a custom
[00:24:10]
type and then encode that, and then how do I decode that?
[00:24:16]
And for encoding it, so you build up these encoders using these variant zero, variant
[00:24:24]
one, variant two functions, which take a name to serialize for that variant on the JavaScript,
[00:24:32]
and then they take the constructor to build it up, so that tells it when it decodes it
[00:24:38]
how to build it up, and then you take the individual codecs for each of the arguments
[00:24:43]
of the custom type.
[00:24:45]
So it really hurts your brain, but I find with understanding any complex pattern in
[00:24:52]
an Elm API, I find it helpful to put yourself in the shoes of the library author and say,
[00:25:00]
like, instead of just saying, like, oh, this is difficult to write, you say, like, what
[00:25:07]
does this API need to accomplish?
[00:25:10]
And then it helps you understand the design.
[00:25:12]
And so again, this API needs, what it needs to accomplish is you need a way to tell it
[00:25:18]
how to, you know, how to take these values, decode them from JSON, and then build up a
[00:25:26]
custom type, and you need to be able to pick apart a custom type and turn it into a serialized
[00:25:32]
JSON object.
[00:25:34]
So I think it helps to keep that in mind when you look at it.
[00:25:37]
Why is it passing in these encoder functions?
[00:25:42]
Often as a library author, you're trying to avoid people from making mistakes.
[00:25:48]
So if it looks a bit odd, it's probably because they're trying to add you to give you some
[00:25:53]
guarantees.
[00:25:54]
And in this case, that is exactly the point.
[00:25:57]
Do you know what the what the resulting JSON looks like?
[00:26:01]
Because as you said, there's a name for the variance.
[00:26:03]
So that's a string that you place.
[00:26:06]
So I'm guessing you have something like a record with an object with a type field where
[00:26:11]
that's...
[00:26:12]
Yeah, I'm trying to remember the exact format, like what the name is.
[00:26:18]
There's one field that's called args.
[00:26:21]
And I think args is just going to have the, you know, if it's variant two, then it's going
[00:26:27]
to have two values in the args list.
[00:26:30]
Or if it's variant zero, I think it's going to have zero values in the args list.
[00:26:34]
And then I don't remember what the actual variant name like teacher or student, that
[00:26:39]
string.
[00:26:40]
I don't remember what that's placed under.
[00:26:43]
Seats or constructor, maybe?
[00:26:45]
I think it might be called something else.
[00:26:47]
So this does tie...
[00:26:51]
So if the library chooses a JSON format for you, then this does not help you with working
[00:26:59]
with external formats.
[00:27:01]
So if you want to, if you want your custom type to look at a certain way, when communicating
[00:27:07]
with HTTP requests, for instance, then it's probably not this that you're going to use.
[00:27:13]
I'm sure that you can use something else.
[00:27:16]
Yeah, you can use one off to do something pretty custom, I think.
[00:27:21]
Yeah.
[00:27:22]
Oh, by the way, I found it.
[00:27:24]
I believe it's called tag.
[00:27:27]
Tag and args.
[00:27:28]
So you get an object that has those two fields.
[00:27:31]
Yeah, that makes sense.
[00:27:33]
So there is one tricky thing that I've encountered when using this library, which is...
[00:27:38]
So sometimes you have certain areas that you don't care to serialize, deserialize.
[00:27:44]
Like you want to send it one way, but not the other.
[00:27:46]
And there is, you can sort of trick it a little bit by creating your own custom...
[00:27:54]
Let me look this up.
[00:27:57]
Yeah, so you can fudge it a little bit because you can use the codec.build function, which
[00:28:03]
is essentially an escape patch.
[00:28:07]
So codec.build takes basically an encoder function, something that takes some value
[00:28:14]
and turns it into an encode value.
[00:28:17]
And it takes as a second argument, a JSON decoder, and you can completely fudge it,
[00:28:22]
right?
[00:28:23]
Like you can turn it into an arbitrary or you can do encode.null or whatever.
[00:28:30]
You can do decode.succeed to any value.
[00:28:33]
So you can sort of use that as an escape patch if you care about sending it, but you don't
[00:28:39]
care about receiving it or vice versa.
[00:28:42]
So that's something I've used.
[00:28:43]
So like one use case that we haven't hit on yet actually, but that I've found this library
[00:28:49]
useful for is using Elm program test.
[00:28:53]
So for Elm program test, you can send...
[00:28:58]
You can make assertions about JS ports that have been sent.
[00:29:03]
And when you do that, you send the JS port as it's some JSON value.
[00:29:09]
And so you can make assertions about that JSON value, but you need a decoder in order
[00:29:14]
to do that.
[00:29:15]
So when you use Elm program test and you want to make assertions about ports, you need to
[00:29:19]
send both an encoder and a decoder?
[00:29:21]
Let's say you're making an assertion about a value that you send to JavaScript through
[00:29:27]
a port in your Elm program test test case.
[00:29:31]
Now when you're making assertions and you say, I expect this outgoing to JavaScript
[00:29:38]
port to have been called with some value.
[00:29:40]
How are you going to assert about that value?
[00:29:42]
You need to have a decoder in your Elm program test set up that tells it how to turn...
[00:29:48]
Because it received some JSON value.
[00:29:51]
And so now you need to be able to decode that.
[00:29:53]
And so that's pretty low level.
[00:29:57]
And it would be a pain to be trying to keep them in sync constantly.
[00:30:05]
And again, this gets to the broader sort of way of thinking, that broader mindset, which
[00:30:12]
is if you notice that you have multiple sources of truth, how can you bring those sources
[00:30:17]
of truth together?
[00:30:18]
So there are fewer places where you could introduce a divergence between those two sources
[00:30:24]
of truth.
[00:30:25]
And so codecs are an example of that concept, but there are many places you can apply that
[00:30:31]
concept.
[00:30:32]
So that's one place that I felt frustrated of having these two sources of truth, that
[00:30:36]
it's like I'm encoding these values, but then I'm making assertions about these values in
[00:30:43]
my Elm program test cases where in fact, I don't even know...
[00:30:48]
I'm going to start having my tests fail if the encode format diverges from the decoder
[00:30:55]
I wrote in my Elm program tests.
[00:30:59]
And those are just two sources of truth that are diverging.
[00:31:02]
So I'd like to have less things that could go wrong there.
[00:31:05]
But again, there are certain things that if I don't want to assert on something, it does
[00:31:09]
get interesting because then you can use those escape patches and do codec.build and give
[00:31:16]
it something that hardcodes either the encoding or the decoding part, depending on what you
[00:31:22]
care about.
[00:31:23]
Yeah.
[00:31:24]
If you do use that, then that is where you would likely need more tests than the rest
[00:31:29]
because this part you can fail.
[00:31:32]
Right.
[00:31:33]
Right.
[00:31:34]
Yeah.
[00:31:35]
Even though you do, even if you use a codec, it's not a bad idea to have some tests around
[00:31:41]
your codecs and it can be useful to have like fuzz tests.
[00:31:46]
It's a really easy fuzz test to write to say that a codec is reversible.
[00:31:50]
It'd be interesting to build up along with the codec to build up a fuzz tester that generates
[00:31:57]
random values for that and then creates a unit test, a fuzz test that it's reversible.
[00:32:04]
You mean with the same API?
[00:32:07]
Yes.
[00:32:08]
Yeah.
[00:32:09]
But then I wonder whether it would actually help assert anything.
[00:32:13]
I guess it would.
[00:32:14]
Yeah.
[00:32:15]
Which is kind of crazy, but it actually could give additional information about correctness.
[00:32:21]
Not necessarily perfect information, but additional information at least.
[00:32:25]
Something interesting.
[00:32:26]
Someone do that.
[00:32:27]
Yeah.
[00:32:28]
It would be a fun experiment.
[00:32:31]
So Leonardo, AKA Mini Bill, gave a talk at the London online meetup recently and he was
[00:32:38]
saying that his initial prototype of the Elm codec library was using this, it included
[00:32:47]
like a form encoder and he resisted the temptation to add additional features and just tried
[00:32:55]
to keep it simple, which I think is a good call.
[00:32:58]
It's usually a good call, yeah.
[00:33:01]
But that does again illustrate that point that this pattern has a lot of different applications.
[00:33:08]
So a form codec, is that for instance when you want to save the result of a form to record
[00:33:17]
somewhere, but you also want to open that form with preexisting data?
[00:33:22]
I think that would be the idea.
[00:33:23]
Yeah.
[00:33:24]
That's interesting.
[00:33:25]
I've never thought of it.
[00:33:28]
Yeah.
[00:33:29]
There are so many applications of this concept.
[00:33:32]
I mean, again, it's just relentlessly looking for ways to reduce sources of truth down to
[00:33:40]
one and to make impossible states impossible.
[00:33:46]
These are sort of the natural logical conclusions of that mindset, I think.
[00:33:51]
Yeah.
[00:33:52]
Another use case I found actually today is for routing.
[00:33:56]
So we're looking at refactoring the code today in the workplace and we have two functions
[00:34:05]
for routing.
[00:34:06]
One to parse the URL into a page or a route.
[00:34:12]
We call that a route in our case.
[00:34:15]
And then a function that takes a route and turns it to a string so that you can create
[00:34:19]
a list.
[00:34:20]
Right.
[00:34:21]
It's very...
[00:34:22]
Yeah.
[00:34:23]
I mean, that must be like almost every single LMSPA must have that function.
[00:34:25]
Yeah.
[00:34:26]
Exactly.
[00:34:27]
Those ones are defined in different places.
[00:34:29]
They're also spread across plenty of modules because we want to...
[00:34:33]
Because at least we split it up.
[00:34:37]
Which might not be a good idea, but we'll see what we'll do about that.
[00:34:42]
And yeah, it's pretty easy for them to get out of sync or even just forgetting to add
[00:34:49]
a route in one place.
[00:34:53]
So that's quite annoying.
[00:34:55]
But if we do that through a codec, then we only have to do it in one place.
[00:34:58]
And that is pretty good, I think.
[00:35:00]
Pretty nice.
[00:35:01]
Right.
[00:35:02]
And that's a good point about if you add a new route, like if you add a new variant to
[00:35:06]
a custom type, then if you're using this sort of codec style pattern where you have a case
[00:35:13]
statement where you build up that encoder, then it keeps you honest there.
[00:35:19]
I guess you would probably have a case statement anyway in the encoder regardless, but you're
[00:35:24]
sharing more information.
[00:35:25]
Yeah, but you might forget to handle the decoding.
[00:35:29]
Right.
[00:35:30]
I guess that's where it becomes more useful, isn't it?
[00:35:34]
On the decoding side, you're less likely to miss it.
[00:35:38]
Interesting.
[00:35:39]
And maybe you can do some stringification for you.
[00:35:43]
Right.
[00:35:44]
Because you can't...
[00:35:45]
Essentially, you cannot have a decoder for which there is no encoder.
[00:35:51]
Or you cannot have an encoder for which there is no decoder.
[00:35:55]
Meaning if you had the encoder, you would probably do case route of variant, variant,
[00:36:01]
variant, variant, and then you encode each of those.
[00:36:03]
And as you said, you're not going to forget to handle a case of those.
[00:36:08]
Assuming you don't do a wildcard, the compiler is going to tell you to handle a new case,
[00:36:13]
and then you'll add the encoder for it.
[00:36:15]
And that's not going to be a problem so much.
[00:36:19]
But then as you said, for the decoder part, you may forget to handle that case where you
[00:36:25]
turn that back into a route.
[00:36:28]
Or for the URL parser part or the JSON decoder or whatever type of codec we're talking about,
[00:36:34]
that's the part you might forget, right?
[00:36:36]
Yeah.
[00:36:37]
If you add the variant to your custom type.
[00:36:40]
In a regular encode decode thing.
[00:36:42]
Right.
[00:36:43]
Exactly.
[00:36:44]
When you have those two things split apart and defined separately.
[00:36:47]
But when you do it as a codec, in order to define a new custom type variant, you now
[00:36:54]
add a pipe chain and you say codec dot variant one or variant zero or depending on how many
[00:37:01]
arguments that custom type has.
[00:37:04]
And and now you get a new argument in that function that gives you that encoder.
[00:37:10]
And that's the only way to access the encoder to encode the value.
[00:37:13]
So you have that case statement.
[00:37:15]
You could handle that case statement, but you don't have the encoder to encode that
[00:37:20]
custom type.
[00:37:21]
So it keeps you honest about keeping those things.
[00:37:23]
So that's that's the really interesting thing about that, isn't it?
[00:37:27]
Yeah.
[00:37:28]
And it's basically the other way around that the compiler will lead you like, oh, you forgot
[00:37:32]
to add this case, this branch in this case of expression.
[00:37:37]
So you add it and then, oh, well, I can't encode it.
[00:37:40]
OK, well, I need to add a variant in the arguments.
[00:37:42]
Oh, well, you need to add a variant in the builder pattern.
[00:37:46]
Mm hmm.
[00:37:47]
And right.
[00:37:48]
You're done.
[00:37:49]
Right.
[00:37:50]
There's another example that that has been on my mind a lot, which is, you know, we've
[00:37:57]
talked on this show about, you know, in Elm pages, how there's this optimized decoder,
[00:38:02]
which will, you know, you you build up a JSON decoder using an API that's exactly like JSON
[00:38:08]
decode, you know, and then when you.
[00:38:11]
Go back to episode one, by the way.
[00:38:14]
Right.
[00:38:15]
Yes.
[00:38:16]
And when you access a field in that optimized decoder, it's exactly like accessing a field
[00:38:22]
in a JSON decoder, except that under the hood, when you run your build, it's tracking which
[00:38:29]
field you're accessing.
[00:38:31]
And then it's actually reserializing that JSON that came in and taking out the fields
[00:38:39]
that you didn't decode, because it can guarantee if you didn't, if you do, you know, decode
[00:38:46]
dot field X and decode dot field Y, but you don't do decode dot field Z and there's a
[00:38:52]
Z in there, then it can throw away Z and the decoder will run exactly the same way because
[00:38:57]
you didn't touch that JSON value.
[00:38:58]
So it takes the the raw JSON value.
[00:39:02]
It tracks which ones you touch by doing decode dot field and then it takes that JSON and
[00:39:09]
then you can strip out all of the unused values.
[00:39:11]
Now that applies not only for unused fields, but for unused indices in JavaScript arrays.
[00:39:18]
And it can do that because if you do like decode dot at four, then you're only accessing
[00:39:26]
index four in this JSON array.
[00:39:29]
It can null out every single other thing.
[00:39:32]
So when you do, when you run it with a regular JSON decoder, it's guaranteed to access the
[00:39:38]
thing that was at four the exact same way because it's at the same index, but you know
[00:39:42]
everything else.
[00:39:43]
So that's actually thought it would be a bit cooler.
[00:39:47]
I thought it would remove the first four items and then change the index of the decoder.
[00:39:58]
Well, I guess it could maybe.
[00:40:01]
You could, it would, that would become pretty tricky.
[00:40:04]
You'd need to store some meta information in order to do that.
[00:40:09]
So like what I do in Elm pages is I run this like when you do Elm pages build, I take the
[00:40:17]
decoder, run it, it tracks what data is used in the JSON, marks those things as being consumed
[00:40:26]
and then it creates a stripped down version of the JSON and then that gets stored as the
[00:40:31]
data that's fetched when you actually run the webpage.
[00:40:34]
So now as a user, when you go and you hit an Elm pages website page, then it's going
[00:40:41]
to get that minify JSON data, but now it's running a vanilla Elm JSON decoder on that.
[00:40:48]
And so it can run the decoder without any special fancy logic.
[00:40:51]
It just runs the decoder and it's guaranteed to decode in an equivalent way.
[00:40:57]
Do you know how much you save by the way, usually?
[00:40:59]
Oh, I mean, well, it depends, right?
[00:41:01]
Yeah.
[00:41:02]
I mean, if it's a giant REST API, I mean, it's not uncommon at all to have deeply nested
[00:41:08]
fields coming back in REST APIs or, you know, 200 different fields.
[00:41:15]
So like I think it would be, I don't think I can claim to accurately give like the median
[00:41:22]
range or something like that, but I will say, imagine that you're getting two fields in
[00:41:29]
a REST response and there are a hundred fields and those fields, some of those fields are,
[00:41:36]
you know, arrays and nested objects and stuff.
[00:41:39]
January me's.
[00:41:41]
Yeah, you can, you could sort of do the mental math and imagine that you could easily have,
[00:41:45]
you know, 2% of the entire data response that you're accessing.
[00:41:50]
It's not, it's not such a stretch of the imagination.
[00:41:53]
So it very much depends on the API.
[00:41:56]
But so that's another interesting application of that idea of using this information that
[00:42:02]
you build up.
[00:42:04]
So you're, you're decoding, but you're also building up information as you decode.
[00:42:09]
And actually, you know, Ilias originally built this library, JSON decode exploration where
[00:42:17]
what it does is it, you can run a JSON decoder and you can give a warning if there are unused
[00:42:23]
JSON fields.
[00:42:25]
That was where I had the idea.
[00:42:27]
So I asked him one day, you know, do you think you'd be able to use this to strip out unused
[00:42:31]
JSON values?
[00:42:32]
And then within like a day he had built a working prototype, which I think ended up
[00:42:38]
pretty much being the release with a couple of like updated docs and test cases after
[00:42:42]
that.
[00:42:43]
But so the sky's the limit with what you can do with this concept of, you know, building
[00:42:48]
up multiple pieces of information at the same time in a pipeline.
[00:42:52]
I'm wondering whether you can do something like that for parsers.
[00:42:56]
Like if you try to parse something like ISO 8601.
[00:43:03]
Your favorite.
[00:43:04]
That's it.
[00:43:05]
Yeah.
[00:43:06]
I'm sorry, it is 8602.
[00:43:10]
Brain dump here.
[00:43:11]
Brain freeze at the last moment.
[00:43:13]
So yeah, if you parse ISO 8601.
[00:43:17]
Which is a date format for people, for new listeners.
[00:43:20]
Because surely you'll know it otherwise.
[00:43:23]
Yeah.
[00:43:24]
So yeah, you parse it, but then you might also just want to rewrite it as a string.
[00:43:32]
Right.
[00:43:33]
Yeah.
[00:43:34]
Yeah.
[00:43:35]
So like for like parsing languages or like in Armoury.
[00:43:39]
Yeah.
[00:43:40]
Yeah.
[00:43:41]
We parse the Arm code and then sometimes we want to rewrite Arm code.
[00:43:46]
Like what could the Arm format work with as a data structure under the domain?
[00:43:51]
I don't know if you can.
[00:43:52]
Yeah, maybe.
[00:43:53]
It's interesting.
[00:43:54]
It is very interesting.
[00:43:57]
Because you can definitely do the same thing.
[00:44:00]
But the question is, would you be able to do it in a way that you, where you would get
[00:44:04]
the same guarantees as what you have with the Arm Codex API?
[00:44:08]
That's fascinating.
[00:44:09]
Well, that's apparently an exercise for the listener.
[00:44:15]
I think it would depend.
[00:44:17]
So if you want to make it reversible, if you want to make parsing a syntax tree reversible,
[00:44:24]
it would need to be a concrete, not an abstract syntax tree.
[00:44:27]
Meaning you would need to keep track of how many white space characters they used.
[00:44:33]
You know, if it's a syntax that supports trailing commas, you'd need to keep track of whether
[00:44:37]
there was a trailing comma, how many spaces were before or after.
[00:44:41]
Anything that could make that syntax.
[00:44:42]
That would need to be part of the data structure you parse it into.
[00:44:48]
I don't think you would need to keep track of every white space, but if you at least
[00:44:54]
keep track of the position of everything, then you can infer things again.
[00:44:58]
Yeah.
[00:44:59]
Yeah.
[00:45:00]
You'd need enough information to be able to infer that.
[00:45:03]
Definitely.
[00:45:04]
You would need to have a lossless format if you wanted it to be truly reversible.
[00:45:11]
But if your goal is not to be reversible, yeah, I wonder how much mileage you would
[00:45:15]
get out of that.
[00:45:17]
It's also, it can also be, the parsing is the harder part.
[00:45:21]
Taking an AST and then printing it out is significantly easier.
[00:45:26]
Right.
[00:45:27]
But you still don't want to make mistakes when serializing.
[00:45:31]
Or do you serializing?
[00:45:33]
You want them to be the same when possible.
[00:45:35]
Right.
[00:45:36]
Although since syntaxes typically aren't changing that frequently, it might be less of an issue
[00:45:43]
for that kind of thing.
[00:45:44]
I wonder.
[00:45:45]
I wonder.
[00:45:46]
But it is an interesting concept.
[00:45:47]
Yeah.
[00:45:48]
Yeah.
[00:45:49]
I mean, if you can build a parser the same way, but with these additional guarantees,
[00:45:56]
that sounds like a win win to me.
[00:45:57]
Yeah.
[00:45:58]
And if you're parsing a phone number and then you know you have a way to turn that back
[00:46:08]
into a phone number in the same format, then if you have confidence that whatever format
[00:46:14]
you're spitting back out can be parsed, that could be kind of nice potentially.
[00:46:19]
So let's say that you parse a phone number and you accept, so if you throw away parentheses
[00:46:28]
and then you parse a digit, parse a digit, parse a digit, throw away a paren, throw
[00:46:33]
away spaces, throw away dashes, parse a digit, parse a digit, whatever.
[00:46:39]
Well now, how are you going to serialize that?
[00:46:44]
You're just going to print out a string of the digits.
[00:46:46]
Or maybe you like...
[00:46:47]
Well, you could remove them as part of the public API, but internally have the type,
[00:46:53]
you know, big type, which does keep all that white space information.
[00:46:59]
Right.
[00:47:00]
Yes, you could.
[00:47:02]
It wouldn't be as performant because you would have a lot of memory that you would maybe
[00:47:08]
not use.
[00:47:09]
I guess it's only useful if you change what you parse.
[00:47:14]
Because if you just want to reprint it, then just keep whatever you parsed originally in
[00:47:21]
memory and then keep it back.
[00:47:24]
But if for instance, if you want to do AST manipulations for Elm syntax, then it can
[00:47:32]
be interesting.
[00:47:33]
Yeah, it's an interesting concept.
[00:47:36]
Yeah, I think that this pattern is ripe for use for sure.
[00:47:42]
There are many places it could apply.
[00:47:44]
It's kind of neat that we're sort of at the beginning of the exploration of this concept.
[00:47:49]
And I think we'll be seeing more and more applications over time.
[00:47:52]
Yeah.
[00:47:53]
Oh, you know what this could be used for?
[00:47:56]
HTML WYSIWYG.
[00:47:57]
Like a lot of people ask, hey, I have this HTML stored in my database.
[00:48:05]
Can I embed it into Elm?
[00:48:07]
Well, you could parse it to HTML and then have that HTML somehow be reversible into
[00:48:15]
a string but also viewable as an HTML from Elm HTML.
[00:48:20]
I guess that can make sense.
[00:48:22]
Interesting.
[00:48:23]
I guess I'm expecting a lot from our listeners now.
[00:48:27]
Yeah, it's very interesting.
[00:48:31]
One thing that we haven't mentioned that I think is worth mentioning is you can also
[00:48:37]
use this technique to encapsulate low level serialization details.
[00:48:42]
For example, if you wanted to serialize something into bytes, again, this is going to be something
[00:48:48]
that you control the serialization and deserialization and are using the same code to do both, like
[00:48:55]
serializing some settings or cached data or whatever it may be.
[00:49:02]
Then it's a lot of work to get a correctly working byte decoder and encoder, which if
[00:49:09]
you're not familiar, I don't know when, like a year ago maybe, this official Elm bytes
[00:49:17]
decoding package was released that allows you to do binary decoding and encoding, which
[00:49:22]
is quite cool.
[00:49:24]
You can pack the data into a much more condensed data format and potentially get some performance
[00:49:30]
gains from that.
[00:49:31]
Although actually, I think that as far as decoding the data, JSON decoders are currently
[00:49:36]
very fast.
[00:49:37]
That may change with WebAssembly at some point.
[00:49:40]
For Elm Review, I still use the JSON optimized version and not the bytes because you can't
[00:49:45]
send bytes over ports for now.
[00:49:49]
Got it.
[00:49:50]
Interesting.
[00:49:51]
Okay, so you have to serialize the bytes to some sort of like base64 encoded string,
[00:49:57]
which represents bytes, but it's not actually directly sending bytes.
[00:50:01]
Which in my case was less performance according to benchmarks.
[00:50:06]
So the decoding and encoding portion may be slower, and it is slower, than if you were
[00:50:13]
decoding and encoding JSON, but the storage is smaller.
[00:50:19]
So one thing that we haven't touched on is that I think that it's a lot of work to get
[00:50:25]
a byte decoder and encoder lined up correctly.
[00:50:28]
So there are certain details, like with bytes, it's much more low level than JSON decoding
[00:50:33]
and encoding.
[00:50:34]
So if you have a list in JSON, it just is going to have an opening array and a closing
[00:50:42]
array notation.
[00:50:43]
For bytes, there's no such thing.
[00:50:46]
You have to tell it when to start looking for a list, when to stop looking for a list,
[00:50:50]
which means that you're typically going to encode, you need to encode the exact number
[00:50:54]
of bytes to consume until you stop treating it as that list.
[00:51:00]
And so that's very low level.
[00:51:02]
But if you're using a package that does like a codec for your bytes, then it can build
[00:51:07]
in that assumption about how it encodes that.
[00:51:09]
So it says, I'm going to encode a number at this position for bytes.
[00:51:15]
And you know, and it encodes, I'm sure it encodes assumptions about like big endian
[00:51:19]
or little endian too, and things like that, right?
[00:51:23]
It's a lot more high level dealing with a package like this.
[00:51:26]
So it's not just that there are fewer things you can get wrong from the decoding encoding
[00:51:30]
perspective, but it can handle some of those low level serialization details.
[00:51:35]
Definitely.
[00:51:36]
Bytes have been out for two years apparently already.
[00:51:38]
Two years, nice.
[00:51:39]
Yeah, in November.
[00:51:40]
So by the time this episode airs, I think it will have been two years.
[00:51:46]
Very nice.
[00:51:47]
We're just starting to see a lot of explorations happening with bytes, I feel like now.
[00:51:51]
Yeah, that'd be pretty cool.
[00:51:53]
Yeah.
[00:51:54]
To see more.
[00:51:55]
Yeah.
[00:51:56]
Well, good stuff.
[00:51:57]
Are there any words of wisdom we want to leave for listeners about how to get started?
[00:52:03]
Where to look for opportunities to use this pattern?
[00:52:06]
I'm not sure I have anything worth sharing here.
[00:52:10]
Is it worth using Elm codec for JSON?
[00:52:13]
And are there opportunities, you know, that you think you could find to use it, you know,
[00:52:19]
in the code bases you work with or?
[00:52:20]
I use it for caching, as I mentioned before, and I think that's a great opportunity for
[00:52:25]
it, especially using Martin's Elm serialize.
[00:52:29]
By the way, shout out to him.
[00:52:31]
He did an amazing job.
[00:52:32]
Yeah.
[00:52:33]
And apart from that, I haven't played much with decoders, with codecs.
[00:52:38]
I think for people who happen to do testing on their ports with Elm program tests, I would
[00:52:44]
definitely recommend that.
[00:52:46]
But otherwise, I think just keep an eye out for opportunities.
[00:52:50]
And you know, especially if you are serializing and deserializing a format, keep an eye out
[00:52:55]
for that.
[00:52:56]
And more broadly, whether it's a codec or not, look out for opportunities to keep track
[00:53:02]
of information as a single source of truth and build up multiple pieces of information
[00:53:08]
at once rather than separately.
[00:53:10]
Yeah, definitely.
[00:53:11]
Yeah, if you can have this, if you have multiple representations of a single thing, then it
[00:53:17]
might be useful to have something like codec or that kind of pattern.
[00:53:22]
Yeah.
[00:53:23]
Cool.
[00:53:24]
And let us know what else you come up with.
[00:53:26]
Tweet us.
[00:53:27]
We'd love to hear what you end up doing with codecs and with that pattern and what instances
[00:53:32]
of that pattern being applied that you've seen.
[00:53:35]
And find us on Twitter and ask us a question.
[00:53:40]
elm.radio.com question.
[00:53:42]
Submit a question and we'll hear from you.
[00:53:45]
All right.
[00:53:46]
Thank you for joining us on Value Room.
[00:53:48]
Until next time.
[00:53:49]
Until next time.