Skip to content

Commit ca0b369

Browse files
committed
update section about kinds
1 parent 502e910 commit ca0b369

1 file changed

Lines changed: 24 additions & 20 deletions

File tree

source_md/making-our-own-types-and-typeclasses.md

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1852,39 +1852,43 @@ Man, that looks weird.
18521852
How would we make a type that could be an instance of that strange typeclass?
18531853
Well, let's look at what its kind would have to be.
18541854
Because `j a` is used as the type of a value that the `tofu` function takes as its parameter, `j a` has to have a kind of `*`.
1855-
We assume `*` for `a` and so we can infer that `j` has to have a kind of `* -> *`.
1856-
We see that `t` has to produce a concrete value too and that it takes two types.
1857-
And knowing that `a` has a kind of `*` and `j` has a kind of `* -> *`, we infer that `t` has to have a kind of `* -> (* -> *) -> *`.
1858-
So it takes a concrete type (`a`), a type constructor that takes one concrete type (`j`) and produces a concrete type.
1855+
It looks like the kind of `a` is probably `*`, but it's not certain, so let's just call it `k` for now.
1856+
We can infer that `j` has to have a kind of `k -> *`.
1857+
We see that `t` has to produce a concrete value too and that it takes two arguments.
1858+
And knowing that `a` has a kind of `k` and `j` has a kind of `k -> *`, we infer that `t` has to have a kind of `k -> (k -> *) -> *`.
18591859
Wow.
18601860

1861-
OK, so let's make a type with a kind of `* -> (* -> *) -> *`.
1861+
Is that actually correct?
1862+
Try `:k Tofu` to find out.
1863+
1864+
OK, so let's make a type with a kind of `k -> (k -> *) -> *`.
18621865
Here's one way of going about it.
18631866

18641867
```{.haskell:hs}
18651868
data Frank a b = Frank {frankField :: b a} deriving (Show)
18661869
```
18671870

1868-
How do we know this type has a kind of `* -> (* -> *) - > *`?
1871+
How do we know this type has a kind of `k -> (k -> *) - > *`?
18691872
Well, fields in ADTs are made to hold values, so they must be of kind `*`, obviously.
1870-
We assume `*` for `a`, which means that `b` takes one type parameter and so its kind is `* -> *`.
1871-
Now we know the kinds of both `a` and `b` and because they're parameters for `Frank`, we see that `Frank` has a kind of `* -> (* -> *) -> *` The first `*` represents `a` and the `(* -> *)` represents `b`.
1873+
We assume `k` for `a`, and `b` takes it as its argument so its kind is `k -> *`.
1874+
Now we know the kinds of both `a` and `b` and because they're parameters for `Frank`, we see that `Frank` has a kind of `k -> (k -> *) -> *` The first `k` represents `a` and the `(k -> *)` represents `b`.
18721875
Let's make some `Frank` values and check out their types.
18731876

18741877
```{.haskell:hs}
18751878
ghci> :t Frank {frankField = Just "HAHA"}
1876-
Frank {frankField = Just "HAHA"} :: Frank [Char] Maybe
1879+
Frank {frankField = Just "HAHA"} :: Frank String Maybe
18771880
ghci> :t Frank {frankField = Node 'a' EmptyTree EmptyTree}
1878-
Frank {frankField = Node 'a' EmptyTree EmptyTree} :: Frank Char Tree
1881+
Frank {frankField = Node 'a' EmptyTree EmptyTree}
1882+
:: Frank Char Tree
18791883
ghci> :t Frank {frankField = "YES"}
18801884
Frank {frankField = "YES"} :: Frank Char []
18811885
```
18821886

18831887
Hmm.
18841888
Because `frankField` has a type of form `a b`, its values must have types that are of a similar form as well.
1885-
So they can be `Just "HAHA"`, which has a type of `Maybe [Char]` or it can have a value of `['Y','E','S']`, which has a type of `[Char]` (if we used our own list type for this, it would have a type of `List Char`).
1889+
So they can be `Just "HAHA"`, which has a type of `Maybe String` or it can have a value of `['Y','E','S']`, which has a type of `[Char]` (if we used our own list type for this, it would have a type of `List Char`).
18861890
And we see that the types of the `Frank` values correspond with the kind for `Frank`.
1887-
`[Char]` has a kind of `*` and `Maybe` has a kind of `* -> *`.
1891+
`String` has a kind of `*` and `Maybe` has a kind of `* -> *`.
18881892
Because in order to have a value, it has to be a concrete type and thus has to be fully applied, every value of `Frank blah blaah` has a kind of `*`.
18891893

18901894
Making `Frank` an instance of `Tofu` is pretty simple.
@@ -1908,32 +1912,32 @@ Let's do some more type-foo.
19081912
We have this data type:
19091913

19101914
```{.haskell:hs}
1911-
data Barry t k p = Barry { yabba :: p, dabba :: t k }
1915+
data Barry t m p = Barry { yabba :: p, dabba :: t m }
19121916
```
19131917

19141918
And now we want to make it an instance of `Functor`.
19151919
`Functor` wants types of kind `* -> *` but `Barry` doesn't look like it has that kind.
19161920
What is the kind of `Barry`?
1917-
Well, we see it takes three type parameters, so it's going to be `something -> something -> something -> *`.
1921+
Well, we see it takes three arguments, so it's going to be `something -> something -> something -> *`.
19181922
It's safe to say that `p` is a concrete type and thus has a kind of `*`.
1919-
For `k`, we assume `*` and so by extension, `t` has a kind of `* -> *`.
1920-
Now let's just replace those kinds with the *somethings* that we used as placeholders and we see it has a kind of `(* -> *) -> * -> * -> *`.
1923+
For `m`, we assume `k` and so by extension, `t` has a kind of `k -> *`.
1924+
Now let's just replace those kinds with the *somethings* that we used as placeholders and we see it has a kind of `(k -> *) -> k -> * -> *`.
19211925
Let's check that with GHCi.
19221926

19231927
```{.haskell:hs}
19241928
ghci> :k Barry
1925-
Barry :: (* -> *) -> * -> * -> *
1929+
Barry :: (k -> *) -> k -> * -> *
19261930
```
19271931

19281932
Ah, we were right.
19291933
How satisfying.
1930-
Now, to make this type a part of `Functor` we have to fill the first two type parameters so that we're left with `* -> *`.
1931-
That means that the start of the instance declaration will be: `instance Functor (Barry a b) where`.
1934+
Now, to make this type a part of `Functor` we have to fill the first two parameters so that we're left with `* -> *`.
1935+
That means that the start of the instance declaration will be: `instance Functor (Barry … …) where`.
19321936
If we look at `fmap` as if it was made specifically for `Barry`, it would have a type of `fmap :: (a -> b) -> Barry c d a -> Barry c d b`, because we just replace the `Functor`'s `f` with `Barry c d`.
19331937
The third type parameter from `Barry` will have to change and we see that it's conveniently in its own field.
19341938

19351939
```{.haskell:hs}
1936-
instance Functor (Barry a b) where
1940+
instance Functor (Barry c d) where
19371941
fmap f (Barry {yabba = x, dabba = y}) = Barry {yabba = f x, dabba = y}
19381942
```
19391943

0 commit comments

Comments
 (0)