From 57aecc46be61e9791f33ce9f7ff6740e8043ae54 Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Sun, 24 Apr 2022 01:09:34 -0700 Subject: [PATCH] Update and publish catamorphism article --- code/catamorphisms/Cata.hs | 11 ++++++++ content/blog/haskell_catamorphisms.md | 39 ++++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/code/catamorphisms/Cata.hs b/code/catamorphisms/Cata.hs index cfbd463..7264690 100644 --- a/code/catamorphisms/Cata.hs +++ b/code/catamorphisms/Cata.hs @@ -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 diff --git a/content/blog/haskell_catamorphisms.md b/content/blog/haskell_catamorphisms.md index 02eb17d..940cce1 100644 --- a/content/blog/haskell_catamorphisms.md +++ b/content/blog/haskell_catamorphisms.md @@ -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 inductive types. +{{< /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