Update and publish catamorphism article
This commit is contained in:
parent
ded50480a8
commit
57aecc46be
|
@ -99,3 +99,14 @@ invert :: BinaryTree a -> BinaryTree a
|
|||
invert = cata $ \case
|
||||
LeafF -> Leaf
|
||||
NodeF a l r -> Node a r l
|
||||
|
||||
data MaybeF a b = NothingF | JustF a deriving Functor
|
||||
|
||||
instance Cata (Maybe a) (MaybeF a) where
|
||||
out Nothing = NothingF
|
||||
out (Just x) = JustF x
|
||||
|
||||
getOrDefault :: a -> Maybe a -> a
|
||||
getOrDefault d = cata $ \case
|
||||
NothingF -> d
|
||||
JustF a -> a
|
||||
|
|
|
@ -1,10 +1,17 @@
|
|||
---
|
||||
title: "Generalizing Folds in Haskell"
|
||||
date: 2022-04-22T12:19:22-07:00
|
||||
draft: true
|
||||
tags: ["Haskell"]
|
||||
---
|
||||
|
||||
Have you encountered Haskell's `foldr` function? Did you know that you can use it to express any
|
||||
function on a list? What's more, there's a way to derive similar functions for
|
||||
{{< sidenote "right" "positive-note" "a large class of data types in Haskell." >}}
|
||||
Specifically, this is the class of <a href="https://en.wikipedia.org/wiki/Inductive_type">inductive types</a>.
|
||||
{{< /sidenote >}}
|
||||
This is precisely the focus of this post.
|
||||
Before we get into the details, it's good to review the underlying concepts in a more familiar setting: functions.
|
||||
|
||||
### Recursive Functions
|
||||
Let's start off with a little bit of a warmup, and take a look at a simple recursive function:
|
||||
`length`. Here's a straightforward definition:
|
||||
|
@ -338,6 +345,36 @@ Given this, here's an implementation of that `invert` function we mentioned earl
|
|||
|
||||
{{< codelines "Haskell" "catamorphisms/Cata.hs" 98 101 >}}
|
||||
|
||||
#### Degenerate Cases
|
||||
|
||||
Actually, the data types we consider don't have to be recursive. We can apply the same
|
||||
procedure of replacing recursive occurrences in a data type's definition with a new type parameter
|
||||
to `Maybe`; the only difference is that now the new parameter will not be used!
|
||||
|
||||
{{< codelines "Haskell" "catamorphisms/Cata.hs" 103 107 >}}
|
||||
|
||||
And then we can define a function on `Maybe` using `cata`:
|
||||
|
||||
{{< codelines "Haskell" "catamorphisms/Cata.hs" 109 112 >}}
|
||||
|
||||
This isn't _really_ useful, since we're still pattern matching on a type that looks
|
||||
identical to `Maybe` itself. There is one reason that I bring it up, though. Remember
|
||||
how `foldr` was equivalent to `cata` for `MyList`, because defining a function `MyListF a -> a` was
|
||||
the same as providing a base case `a` and a "combining function" `Int -> a -> a`? Well,
|
||||
defining a function `MaybeF x a -> a` is the same as providing a base case `a` (for `NothingF`)
|
||||
and a handler for the contained value, `x -> a`. So we might imagine the `foldr` function for `Maybe`
|
||||
to have type:
|
||||
|
||||
```Haskell
|
||||
maybeFold :: a -> (x -> a) -> Maybe x -> a
|
||||
```
|
||||
|
||||
This is exactly the function [`maybe` from `Data.Maybe`](https://hackage.haskell.org/package/base-4.16.1.0/docs/Data-Maybe.html#v:maybe)! Hopefully you can follow a similar process in your head to arrive at "fold"
|
||||
functions for `Either` and `Bool`. Indeed, there are functions that correspond to these data types
|
||||
in the Haskell standard library, named [`either`](https://hackage.haskell.org/package/base-4.16.1.0/docs/Data-Either.html#v:either) and [`bool`](https://hackage.haskell.org/package/base-4.16.1.0/docs/Data-Bool.html#v:bool).
|
||||
Much like `fold` can be used to represent any function on lists, `maybe`, `either`, and `bool` can be
|
||||
used to represent any function on their corresponding data types. I think that's neat.
|
||||
|
||||
#### What About `Foldable`?
|
||||
If you've been around the Haskell ecosystem, you may know the `Foldable` type class.
|
||||
Isn't this exactly what we've been working towards here? No, not at all. Take
|
||||
|
|
Loading…
Reference in New Issue
Block a user