Union Quack.
When we are not amused by other peoples’ types.
me> Wat.
wat> Say it one more time motherquacker! 🔫 🦆
me> Remember we talked about type alias that other time? In the end, we touched upon making your own types…
wat> Sure. Why would you want to make your own types then? Are existing types too good for you?
me> As a matter of fact, I’m not sure. I’m pretty happy with the types I know already. I have Bool
for logic stuff, Int
and Float
to play with numbers, String
to write, List
when I want to have many things of the same type, tuples and records when I need to put some different things together…
wat> You do. But sometimes they’re not good enough. Say I want to represent votes for a simple yes or no question ; there are two possibilities: yes, and no. How would you represent your votes with the types you have?
me> Oh, that’s easy. I could have a Bool
value that’s True
for yes, and False
for no!
wat> Ok, let’s do this with a type alias then:
type alias Vote = Bool
wat> But now there’s a problem because I want also to know when the vote hasn’t been cast yet…
me> Hmm… ok, that won’t work. Say I make it an Int
and when it’s 1
it’s yes, and when it’s 2
it’s no and when it’s 0
it’s undecided…
wat> No points for clarity.
me> Hmm… yeah. Then a String
is the obvious choice. I can say that "yes"
is yes, and "no"
is no and…
wat> …and "quack"
is?
me> Not gonna happen? 😅 Ok, I see your point. A String
can hold expressive values, but there’s no way to restrict what it holds to what I need.
wat> And it’s likely to force you to consider cases that should never bother you in the first place. So let’s give that Vote
type its own, brand new shape:
type Vote
= Yes
| No
| Undecided
me> So, that’s a real type right? It doesn’t say alias.
wat> Indeed. And it’s what we can call a “union” type, because the possible values form some kind of set.
me> So a value of this type can be either Yes
, No
or Undecided
, and nothing else? No weird, unaccounted values to deal with? No quack?
wat> No quack. Or maybe quack, if we decided to add it to the set of possible values. For example, say I not only want to account for undecided votes, but also for blank votes, and that those two notions are not the same:
type Vote
= Yes
| No
| Undecided
| Blank
me> Ok. So, how can I use this type?
wat> Say I want to give any vote a string representation ; "voted yes"
, "voted no"
, "still undecided"
and "blank bulletin"
, I will need to know what the vote was to decide what string to return, like this:
voteToString : Vote -> String
voteToString vote =
case vote of
Yes ->
"voted yes"
No ->
"voted no"
Undecided ->
"still undecided"
Blank ->
"blank bulletin"myVote : Vote
myVote = YesvoteToString myVote
=> "voted yes"voteToString Undecided
=> "still undecided"
me> So, this is saying in case vote
is Yes
, say "voted yes"
, in case it’s No
, say "voted no"
…
wat> Yes. This is called pattern matching. It also works with other types too, like String
, Int
, List
, …
me> Ok, so now I can make my own types with the values I need and only those. That’s cool. We’ll see about your pattern matching later if you will.
wat> But there is so much more to discover there…
me> Next time 😴