post(typoclassopedia): Traversable
This commit is contained in:
parent
b924a77488
commit
d3ebc8b77d
@ -918,3 +918,133 @@ class Foldable t where
|
||||
```
|
||||
|
||||
The additional constraint for implementing `traverse_` in terms of `sequenceA_` is the requirement of the `Foldable` instance `t` to be a `Functor` as well.
|
||||
|
||||
Traversable
|
||||
===========
|
||||
|
||||
## Intuition
|
||||
|
||||
```haskell
|
||||
traverse :: Applicative f => (a -> f b) -> t a -> f (t b)
|
||||
sequenceA :: Applicative f => t (f a) -> f (t a)
|
||||
```
|
||||
|
||||
### Exercises
|
||||
|
||||
1. There are at least two natural ways to turn a tree of lists into a list of trees. What are they, and why?
|
||||
|
||||
Note: I'm not really sure whether my solution is _natural_, I think the question is rather ambiguous in the sense that it's not clear whether the trees in the final list of trees can have lists as their values, i.e. `Tree [Int] -> [Tree [Int]]` is valid or only `Tree [Int] -> [Tree Int]` is, but let me know if you think otherwise.
|
||||
|
||||
**Solution**:
|
||||
|
||||
One way is to put each `Node`, `Leaf` or `Empty` in a list in-order, this way the structure of the tree can be recovered from the list, here is a quick sketch (`[]` is an arbitrary list):
|
||||
|
||||
![tree to list](/img/typoclassopedia/tree.jpg)
|
||||
|
||||
```haskell
|
||||
let tree = Node (Node (Leaf []) Empty) [] (Leaf [])
|
||||
let list = [Node Empty [] Empty, Node Empty [] Empty, Leaf [], Empty, Leaf []]
|
||||
```
|
||||
|
||||
2. Give a natural way to turn a list of trees into a tree of lists.
|
||||
|
||||
**Solution**:
|
||||
|
||||
To recover the original tree from the list of trees, whenever we encounter a `Node` in the list, we catch the next three values as left, value, and right nodes of the original node.
|
||||
|
||||
3. What is the type of `traverse . traverse`? What does it do?
|
||||
|
||||
**Solution**:
|
||||
|
||||
```haskell
|
||||
(traverse . traverse) :: Applicative f => (a -> f b) -> t (t2 a) -> f (t (t2 b))
|
||||
```
|
||||
|
||||
It traverses on a deeper level, retaining the structure of the first level.
|
||||
|
||||
4. Implement `traverse` in terms of `sequenceA`, and vice versa.
|
||||
|
||||
**Solution**:
|
||||
|
||||
```haskell
|
||||
sequenceA = traverse id
|
||||
|
||||
traverseA f c = sequenceA (fmap f c)
|
||||
```
|
||||
|
||||
## Instances and examples
|
||||
|
||||
### Exercises
|
||||
|
||||
1. Implement `fmap` and `foldMap` using only the `Traversable` methods. (Note that the `Traversable` module provides these implementations as `fmapDefault` and `foldMapDefault`.)
|
||||
|
||||
**Solution**:
|
||||
|
||||
```haskell
|
||||
newtype Id a = Id { getId :: a }
|
||||
|
||||
instance Functor Id where
|
||||
fmap f (Id x) = Id (f x)
|
||||
|
||||
instance Applicative Id where
|
||||
pure x = Id x
|
||||
(Id f) <*> (Id x) = Id (f x)
|
||||
|
||||
fmapDefault :: Traversable t => (a -> b) -> t a -> t b
|
||||
fmapDefault f = getId . traverse (Id . f)
|
||||
|
||||
foldMapDefault :: (Monoid m, Traversable t) => (a -> m) -> t a -> m
|
||||
foldMapDefault f = getConst . traverse (Const . f)
|
||||
```
|
||||
|
||||
See the [Const](https://www.stackage.org/haddock/lts-9.9/base-4.9.1.0/src/Data-Functor-Const.html#Const) Functor's definition for intuition.
|
||||
|
||||
2. Implement `Traversable` instances for `[]`, `Maybe`, `((,) a)`, and `Either a`.
|
||||
|
||||
**Solution**:
|
||||
|
||||
```haskell
|
||||
instance Traversable [] where
|
||||
traverse :: Applicative f => (a -> f b) -> [a] -> f [b]
|
||||
traverse _ [] = pure []
|
||||
traverse f (x:xs) = (:) <$> f x <*> Main.traverse f xs
|
||||
|
||||
instance Traversable Maybe where
|
||||
traverse :: Applicative f => (a -> f b) -> Maybe a -> f (Maybe b)
|
||||
|
||||
traverse _ Nothing = pure Nothing
|
||||
traverse f (Just x) = Just <$> f x
|
||||
|
||||
instance Traversable ((,) c) where
|
||||
traverse :: Applicative f => (a -> f b) -> (c, a) -> f (c, b)
|
||||
|
||||
traverse f (c, a) = (,) c <$> f a
|
||||
|
||||
instance Traversable (Either c) where
|
||||
traverse :: Applicative f => (a -> f b) -> Either c a -> f (Either c b)
|
||||
|
||||
traverse _ (Left c) = pure (Left c)
|
||||
traverse f (Right a) = Right <$> f a
|
||||
```
|
||||
|
||||
3. Explain why `Set` is `Foldable` but not `Traversable`.
|
||||
|
||||
**Solution**:
|
||||
|
||||
First, in terms of laws, `Set` is not a `Functor`, thus it cannot be made into a `Traversable` instance, since `Traversable` instances require `Functor` superclasses.
|
||||
|
||||
Second, on an intuitive level: In `Foldable`, the goal is not to keep the shape/structure of the original container, we are trying to reduce the container into some value, and the shape of the final result doesn't matter, but in `Traversable`, we ought to keep the structure of the final result, but we can't guarantee this while using `Set`s, because we can define some transformation `f :: Set a -> Set a` which reduces the length of the `Set`.
|
||||
|
||||
|
||||
See [Foldable vs. Traversable](https://stackoverflow.com/questions/35857733/foldable-vs-traversable) and [Sets, Functors and Eq confusion](https://stackoverflow.com/questions/19177125/sets-functors-and-eq-confusion). and [Foldable and Traversable](https://wiki.haskell.org/Foldable_and_Traversable) for more details.
|
||||
|
||||
4. Show that `Traversable` functors compose: that is, implement an instance for `Traversable (Compose f g)` given `Traversable` instances for `f` and `g`.
|
||||
|
||||
**Solution**:
|
||||
|
||||
```haskell
|
||||
instance (Traversable f, Traversable g) => Traversable (Compose f g) where
|
||||
traverse :: (Applicative f) => (a -> f b) -> Compose g h a -> f (Compose g h b)
|
||||
traverse f (Compose t) = Compose <$> traverse (traverse f) t
|
||||
```
|
||||
|
||||
|
BIN
img/typoclassopedia/tree.jpg
Normal file
BIN
img/typoclassopedia/tree.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 53 KiB |
Loading…
Reference in New Issue
Block a user