All over the map(s)
Where we learn that map
is not just for lists (and about a forbidden f-word)
me> Wat?
wat> Hmm?
me> Remember we talked about Maybe
? And you showed me this Maybe.map
function to take what’s in it so we can work with it (and just continue when there’s nothing)?
wat> Sure ; what about it?
me> Is there a reason why I find map
also in Array
, List
, …
wat> There is 😃 look at their signature:
Maybe.map : (a -> b) -> Maybe a -> Maybe b
List.map : (a -> b) -> List a -> List b
Array.map : (a -> b) -> Array a -> Array b
me> 😮 They look all the same! It’s like:
Something.map : (a -> b) -> Something a -> Something b
wat> 👏 looks like there is a pattern indeed (and you can see in every package that has a map
function that it’s a general trend). What do you think?
me> So… it’s like all of these are data structures that have something inside and their map
function allows you to take what’s inside and apply a function, re-packaging the result into the structure… but a Maybe
, a List
or an Array
don’t necessarily have something inside… and a List
and an Array
may hold many things inside.
wat> That’s why they each have their own map
functions. Maybe.map
knows how to take what is inside the Maybe
(if anything) and how to repack the new value into a Just
, or else that it needs to return Nothing
. List.map
and Array.map
know how to iterate on each element, and repackage all the new values in the same order.
me> Ok… so map
is a kind of generic name for “take what’s inside and apply a transformation, and repackage the results into the structure”?
wat> Kiiiiiind of… consider Generator
…
me> What’s a Generator
?
wat> It’s a type that we can use to get a value randomly generated. You can look at it like a deck of cards or a set of dice that can pick anything you want at random. We’re not going to discuss how to use it (yet) ; for our purpose today, if you’ve got a Generator a
, you have some kind of promise that when you use it you’ll get an a
each time.
me> Ooooook. So, a Generator a
is like having an a
in the future?
wat> Precisely. And Generator
has its map
function too! Can you guess its signature?
me> Hmm…
Generator.map : (a -> b) -> Generator a -> Generator b
me> Is that the Generator.map
you’re looking for?
wat> 😄
me> Soooo… aside from the increasingly familiar a -> b
transformation function, it takes our Generator a
that can produce values of type a
and give us back a Generator b
that can produce values of type b
… but… how can it transform a future value?
wat> That’s the trick the people who invented the Generator
type figured out for you 😉 (you can look at the function’s source if you want to know how they did it) ; what I wanted you to see is that map
does not necessarily need for something to be “inside” the type it works with. It can be something that’s “promised” too. And it’s not just Generator
:
Cmd.map : (a -> b) -> Cmd a -> Cmd b
Sub.map : (a -> b) -> Sub a -> Sub b
Html.map : (a -> b) -> Html a -> Html b
wat> A Cmd a
is a command that may produce messages of type a
, a Sub a
is a subscription to events that might produce messages of type a
and an Html a
is a structure that represents an html dom tree that can (🎉 surprise!) produce messages of type a
…
me> And their map
function allows you to change the messages they produce from a type a
into new messages of type b
? Just like Generator.map
?
wat> Yep 😃 there is even a word for those types that have a map
function…
me> Ooook. What is it?
wat> *mumbles unintelligibly something about the f-word*
me> What, *that* f-word? 🍆
wat> Nah. Worse. A word so terrifying that the Elm creators dared not have it spoken aloud, in fear that the coding masses flee their creation.
me> Oh, come on wat, it will stay between us.
wat> It’s Functor.
me> Func-what?
wat> Functor. I know, I know, it sounds like a made-up word (which it is (like any other word, really)). It’s one of those words mathematicians and computer scientists love to invent. But when you read or hear it, you can almost always replace it mentally by “that which can be map
-ed upon”.
me> So, that’s all it means? Something that has a map
function for?
wat> Almost. To be a well-behaved Functor, you must abide by some rules ; I’ll let you discover them by yourself if you like, but beware: they often are expressed in terms specific to category theory, so you might want to get the gist of that first. And anyways, you most probably won’t need to know them on a daily basis to guess what a map
function does, nor to use it proficiently.
me> Why would I need them then?
wat> They may be useful when you create a map
on a custom data type, and don’t want to end up with something too wonky.
me> Ok. Well, I guess that’ll be enough for now…
wat> Quack to you soon 🦆