|
|
@ -0,0 +1,111 @@
|
|
|
1
|
# Typeclassopedia: A journey
|
|
|
2
|
|
|
|
3
|
I don't think Haskell is scary, but there still is a lot to learn after
|
|
|
4
|
you finish LYAH. I kind of stopped there, because I was still scared and
|
|
|
5
|
because it felt so good already, why continue?
|
|
|
6
|
|
|
|
7
|
Well, because Haskell is so much more and I think it's one of the things
|
|
|
8
|
that make Haskell special: Working with high-level abstractions that
|
|
|
9
|
make things possible that were previously unthinkable.
|
|
|
10
|
|
|
|
11
|
I think I've missed a lot so far, because if you "just use" Haskell you
|
|
|
12
|
aren't really in control, one might even say that you aren't really
|
|
|
13
|
writing Haskell, you've stopped right after the doorstep.
|
|
|
14
|
|
|
|
15
|
So, let's find out what's behind the doorstep.
|
|
|
16
|
|
|
|
17
|
## Functors
|
|
|
18
|
|
|
|
19
|
> class Functor f where
|
|
|
20
|
> fmap :: (a -> b) -> f a -> f b
|
|
|
21
|
|
|
|
22
|
Instances for `Either e` and `((->) e)`.
|
|
|
23
|
|
|
|
24
|
Mhh, `Either e` seems easier, although it also looks a bit weird:
|
|
|
25
|
`Either` is usually about two things, the `l` and the `r` part.
|
|
|
26
|
|
|
|
27
|
Ok, so why are we declaring `Either e` the `Functor` instance, why not
|
|
|
28
|
just `Either`? (Excuse my ignorance here, I've really been avoiding this
|
|
|
29
|
stuff, so I'll have to be very explicit about everything for a while.)
|
|
|
30
|
|
|
|
31
|
Well, let's have a look at the `fmap` again, then. It's type is `(a ->
|
|
|
32
|
b) -> f a -> f b`, so for `Either e` it is `(a -> b) -> Either e a ->
|
|
|
33
|
Either e b`. That's a hint why it is this way: the `l` (here `e`) part
|
|
|
34
|
is usually used for errors and `fmap`ing over that is probably not very
|
|
|
35
|
interesting most of the time. So we "change" the `r` part.
|
|
|
36
|
|
|
|
37
|
But still, *can* we define a `Functor` instance for `Either`? The type
|
|
|
38
|
would be `(a -> b) -> Either a -> Either b`. I think that would not
|
|
|
39
|
work, because we haven't "applied" `Either` fully, so likely Haskell
|
|
|
40
|
will not like it.
|
|
|
41
|
|
|
|
42
|
But what does `Either l` mean? It's incomplete, but we if we "apply"
|
|
|
43
|
some other type to it then we have a type we can use. Is this what kinds
|
|
|
44
|
are? Some kind of marker for such incomplete types? Is there a name for
|
|
|
45
|
those things?
|
|
|
46
|
|
|
|
47
|
Playing with this shows that those things really aren't allowed:
|
|
|
48
|
|
|
|
49
|
```
|
|
|
50
|
λ :kind Either
|
|
|
51
|
* -> * -> Either
|
|
|
52
|
λ :kind Either Int
|
|
|
53
|
* -> *
|
|
|
54
|
λ Either Int -> Either Int
|
|
|
55
|
<interactive>:1:1:
|
|
|
56
|
Expecting one more argument to `Either Int'
|
|
|
57
|
In a type in a GHCi command: Either Int -> Either Int
|
|
|
58
|
```
|
|
|
59
|
|
|
|
60
|
First, `Either` is a *type constructor*, e.g. you can't use it right
|
|
|
61
|
away, but you build types with it. You can, however, also apply it
|
|
|
62
|
partially and only later decide on the other parameters.
|
|
|
63
|
|
|
|
64
|
For example, we can have `WithError`, which is just `Either String`.
|
|
|
65
|
But to use it we still have to "apply" it to some other type.
|
|
|
66
|
|
|
|
67
|
> type WithError = Either String -- "incomplete"
|
|
|
68
|
>
|
|
|
69
|
> zeroIsSpecial :: Int -> WithError Int
|
|
|
70
|
> zeroIsSpecial 0 = Left "you can't have my precious zero, sorry"
|
|
|
71
|
> zeroIsSpecial n = Right n
|
|
|
72
|
|
|
|
73
|
The [page on Kinds][kinds] on the Haskell wiki tells me that those
|
|
|
74
|
complete types, e.g. types with kind `*` are called monomorphic (data)
|
|
|
75
|
types. Are there polymorphic data types? Well, there surely are, but we
|
|
|
76
|
can't compute with them. (Surely? Yes, `Either`, for example. But what
|
|
|
77
|
about ordinary constructors with two arguments? Mhh.. ok, haskell says
|
|
|
78
|
they aren't type constructors, so they have no kind.)
|
|
|
79
|
|
|
|
80
|
[kinds]: http://www.haskell.org/haskellwiki/Kind
|
|
|
81
|
|
|
|
82
|
Ok, let's come back to this later.
|
|
|
83
|
|
|
|
84
|
So, let's define a `Functor` instance for `Either e`. But first, what
|
|
|
85
|
will it "mean"? If we make something a Functor, then we look at it as a
|
|
|
86
|
whole, so `Either e` as an instance of `Functor` is not two parts, but
|
|
|
87
|
one. It's a container for things. And to the value in that container we
|
|
|
88
|
want to apply a function. Let's do that first.
|
|
|
89
|
|
|
|
90
|
> instance Functor (Either e) where
|
|
|
91
|
> fmap f (Right r) = Right $ f r
|
|
|
92
|
> fmap f l = l
|
|
|
93
|
|
|
|
94
|
That was... easy. Am I missing something?
|
|
|
95
|
|
|
|
96
|
What about `((->) e)` then? That seems trickier. First, `(->)`s kind is
|
|
|
97
|
`* -> * -> *`, just as `Either`s was, but I find it a little bit more
|
|
|
98
|
difficult to see what that means.
|
|
|
99
|
|
|
|
100
|
For `Either` it was that we had to supply the types for the `Left` and
|
|
|
101
|
`Right` parts. And for `(->)` it means which arguments we want the
|
|
|
102
|
function type to have. So, `((->) e)` means something with an `e` as an
|
|
|
103
|
argument.
|
|
|
104
|
|
|
|
105
|
The Typeclassopedia mentions that the `e` is read-only and that this
|
|
|
106
|
type is also known as `Reader`, so let's look at what it does.
|
|
|
107
|
|
|
|
108
|
> instance Functor ((->) e) where
|
|
|
109
|
> fmap :: (a -> b) -> (((->) e) a) -> (((->) e) b)
|
|
|
110
|
> -- aka (a -> b) -> (e -> a) -> (e -> b)
|
|
|
111
|
> fmap f g =
|