Generator-ception
Where we andThen
a Generator
inside a Generator
me> So, Random
has an andThen
function… 😄
wat> That it does 😉 would you like to know more?
me> Sure. Last time we saw andThen
it was for Maybe
and Result
, it allowed us to access what was kept inside a "successful" value of a Maybe
or a Result
(if any) and gave us the ability to either continue on a successful value, or switch to an error value, allowing us to chain computations that could fail. So far, so good. But this frame of reference makes no sense with a Generator
that can’t ever fail now, does it?
wat> Smart question 😃 the thing is, andThen
is not just about success and failure, just as map
is not just about taking a value that is actually already there (but wrapped inside a constructor) so we can changing it without having to do the un-wrapping. The actual behaviour of map map
changes depending on the context, and so does andThen
’s.
me> This is kinda confusing… 😵
wat> It is, just as any new concept should 😉 But the good news is that it’s going to get clearer and clearer as we find more examples.
me> Ok. So, what’s the point of andThen
for Generator
?
wat> First, let’s remember what a Generator a
is…
me> It’s sort of a template to make a random a
when we ask the framework to make one for us?
wat> That’s a definition we can work with. And what did map
allow us to do with a generator?
Random.map : (a -> b) -> Generator a -> Generator b
me> To make a new Generator b
from a Generator a
using an a -> b
transformation function which will take the value generated by the Generator a
to produce the b
that will be generated by our shiny new Generator b
?
wat> Exactly! 😄 You can picturemap
as a way to take what’s inside a box, and change it, keeping the same box. Now, let’s get to andThen
:
Random.andThen : (a -> Generator b) -> Generator a -> Generator b
me> So… if you give me a Generator a
and an a -> Generator b
function, it gives us a Generator b
… so it’s a bit like map
, but we have to return the Generator b
with the transformation? Not just a b
to re-wrap it in a Generator b
box?
wat> Yes. Where map
only allows you to change the content of the box, andThen
allows you to change the box itself.
me> Ok… but why would I need to change the box itself? 🤔
wat> Let’s see… say I want to generate a random string of random length and that I have these generators:
gimmePositiveInt : Int -> Generator Int
gimmeStringOfLength : Int -> Generator String
me> Sooo… I guess gimmePositiveInt
will give me a Generator Int
which gives only positive Int
up to a given value, while gimmeStringOfLength
generates a String
that’s as long as a given Int
value?
wat> Indeed. Now how would you go about generating your string of random length using only those two generators and Random.map
?
me> Like this?
gimmeRandomStringOfUpTo10Chars =
gimmePositiveInt 10
|> Random.map (\n -> gimmeStringOfLength n)
wat> Well, sadly that will not give you what you think it should… what is the type of your gimmeRandomStringOfUpTo10Chars
result?…
gimmeRandomStringOfUpTo10Chars : Generator (Generator String)
gimmeRandomStringOfUpTo10Chars =
gimmePositiveInt 10
|> Random.map (\n -> gimmeStringOfLength n)
wat> Generatorception 😉
me> Er… ok, I’ve got this. We gave an Int -> Generator String
to map
:
Random.map : (a -> b) -> Generator a -> Generator b
-- with (a -> b) being (Int -> Generator String) we get
-- a = Int
-- b = Generator String
-- so the result is now Generator (Generator String)
-- and we'd like it to be just Generator String
wat> Yup. Now, let’s try with andThen
instead…
gimmeRandomStringOfUpTo10Chars : Generator String
gimmeRandomStringOfUpTo10Chars =
gimmePositiveInt 10
|> Random.andThen (\n -> gimmeStringOfLength n)Random.andThen : (a -> Generator b) -> Generator a -> Generator b
-- with (a -> b) being (Int -> Generator String) we get
-- a = Int
-- b = Generator String
-- so the result is now Generator String
-- and that's what we wanted :)
me> Ok… the way I see it, we’ll use map when we just want to change the value the Generator
produce’s without having to get another random value, and andThen
when we do want another random value. Is that correct?
wat> That’s pretty much it 😃
me> And so Generator
is at the same time a Functor (it has a map
function) and a Monad? (it has an andThen
function)
wat> Bravo 👏
me> Well, I guess I have pretty much all I need to make random stuff. Read you next time?
wat> Read you next time 😉