Add more drafts
This commit is contained in:
parent
1a6f5e061b
commit
b492c2d0fb
93
content/blog/haskell_newtype.md
Normal file
93
content/blog/haskell_newtype.md
Normal file
|
@ -0,0 +1,93 @@
|
|||
---
|
||||
title: "Multiple Typeclass Instances using Newtype in Haskell"
|
||||
date: 2022-04-28T23:09:42-07:00
|
||||
tags: ["Haskell"]
|
||||
draft: true
|
||||
---
|
||||
|
||||
> And I have known the eyes already, known them all—
|
||||
>
|
||||
> The eyes that fix you in a formulated phrase . . .
|
||||
|
||||
It sometimes feels like the data types that we work with are simply
|
||||
too locked down through their typeclass instances. A type might have multiple
|
||||
law-abiding implementations of the required methods, and yet only one implementation can be used
|
||||
in an `instance` declaration. First, let's take a look at some type classes and types
|
||||
for which this is true, starting with `Monoid`.
|
||||
|
||||
### The `Monoid` Type Class
|
||||
Recall that for a type `m` with `Monoid m`, the following things are available:
|
||||
|
||||
* An associative binary operation, `(<>)`. Associativity means that for any
|
||||
three elements of `m`, like `a`, `b`, and `c`, the following
|
||||
holds:
|
||||
|
||||
```Haskell
|
||||
(a <> b) <> c == a <> (b <> c)
|
||||
```
|
||||
* An neutral element under `(<>)`, called `mempty`. It being the neutral element
|
||||
means that using it with `(<>)` doesn't do anything. For any `a :: m`, we
|
||||
can (with minor notation abuse) write the following:
|
||||
|
||||
```Haskell
|
||||
a <> mempty == mempty <> a == a
|
||||
```
|
||||
|
||||
This is a pretty broad specification, actually. Many types have multiple different
|
||||
operations that satisfy the `Monoid` laws. Let's take a look at a few of them,
|
||||
starting with integers.
|
||||
|
||||
#### Integers
|
||||
Addition is an associative binary operation. Furthermore, it's well-known that adding zero to a
|
||||
number leaves that number intact: \\(0+n = n + 0 = n\\). So we might define a `Monoid` instance for
|
||||
numbers as follows. Note that we actually provide `(<>)` via the `Semigroup` class,
|
||||
which _just_ requires the associative binary operation, and serves as a superclass for `Monoid`.
|
||||
|
||||
```Haskell
|
||||
instance Semigroup Int where
|
||||
(<>) = (+)
|
||||
|
||||
instance Monoid Int where
|
||||
mempty = 0
|
||||
```
|
||||
|
||||
Cool and good. But hey, there are other binary operations on integers! What about
|
||||
multiplication? It is also associative, and again it is well-known that multiplying
|
||||
anything by one leaves that number as it was: \\(1\*n = n\*1 = n\\). The corresponding
|
||||
`Monoid` instance would be something like the following:
|
||||
|
||||
```Haskell
|
||||
instance Semigroup Int where
|
||||
(<>) = (*)
|
||||
|
||||
instance Monoid Int where
|
||||
mempty = 1
|
||||
```
|
||||
|
||||
But we can't have both. Haskell yells at us:
|
||||
|
||||
```
|
||||
Duplicate instance declarations:
|
||||
instance Semigroup Int -- Defined at test.hs:1:10
|
||||
instance Semigroup Int -- Defined at test.hs:7:10
|
||||
|
||||
Duplicate instance declarations:
|
||||
instance Monoid Int -- Defined at test.hs:4:10
|
||||
instance Monoid Int -- Defined at test.hs:10:10
|
||||
```
|
||||
|
||||
Okay, so we can have at most one. But that's not good.
|
||||
Fortunately, thanks to the `Num` instance for `Int`, we get
|
||||
functions that are pretty much the same as `fold`, except
|
||||
specialized to multiplication and addition:
|
||||
|
||||
```Haskell
|
||||
fold :: (Foldable t, Monoid m) => t m -> m
|
||||
product :: (Foldable t, Num a) => t a -> a
|
||||
sum :: (Foldable t, Num a) => t a -> a
|
||||
```
|
||||
|
||||
This takes care of _most_ of the uses we have for `(+)` and `(*)`;
|
||||
it does, however, prevent us from using `Int` with [`MonadWriter`](https://hackage.haskell.org/package/mtl-2.2.2/docs/Control-Monad-Writer-Lazy.html#t:MonadWriter).
|
||||
|
||||
#### Booleans
|
125
content/blog/typeclasses_are_logic.md
Normal file
125
content/blog/typeclasses_are_logic.md
Normal file
|
@ -0,0 +1,125 @@
|
|||
---
|
||||
title: Typeclasses are Basically Logic Programming
|
||||
date: 2021-06-28T17:11:38-07:00
|
||||
draft: true
|
||||
description: "In this post, we explore the connection between typeclasses and logic programming."
|
||||
tags: ["Haskell", "Prolog"]
|
||||
---
|
||||
|
||||
I recently spent some time playing around with the implementation of
|
||||
Haskell-like typeclasses in a toy functional language. In doing so
|
||||
I noticed how similar an implementation of a little logic programming
|
||||
language is to an implementation of type class search. Though this
|
||||
may be very well known, I thought I'd take some time to describe
|
||||
the similarities (and the differences).
|
||||
|
||||
### Kicking off the `Show`
|
||||
Let's begin with a very simple typeclass: `Show`. When I was a TA
|
||||
for my university's programming languages course, we mentioned
|
||||
this typeclass to the students, despite considering typeclasses
|
||||
as a whole to be a topic beyond our scope. Four our purposes, we
|
||||
can consider it to be a class with only one method: `show`.
|
||||
|
||||
```Haskell
|
||||
class Show a where
|
||||
show :: a -> String
|
||||
```
|
||||
|
||||
An instance of `Show` may look as follows:
|
||||
|
||||
```Haskell
|
||||
instance Show () where
|
||||
show () = "()"
|
||||
```
|
||||
|
||||
So far, we haven't done much: we let Haskell know of a typeclass `Show`,
|
||||
and also that `()` has an implementation of show. One might write
|
||||
this in Prolog as follows:
|
||||
|
||||
{{< codelines "Prolog" "typeclass-prolog/kb.pl" 1 1 >}}
|
||||
|
||||
Wait a moment, something was lost intranslation. What about `show`,
|
||||
and its implementation? We did not carry these over into Prolog.
|
||||
A truly equivalent formulation in Prolog would contain this
|
||||
information; however, for simplicity,
|
||||
{{< sidenote "right" "pretend-note" "we'll pretend that" >}}
|
||||
I'd argue that our little make-believe is morally correct. We
|
||||
can think of it in this way: to be allowed to write down
|
||||
the equivalent Prolog rule you need to provide all the required methods,
|
||||
and the proof that you provided them is recorded alongside the
|
||||
rule. A sort of entrance ticket, if you will.
|
||||
{{< /sidenote >}}
|
||||
the line of code above, and any further lines of code of that sort, are
|
||||
endowed with the implementation of all the required methods.
|
||||
|
||||
On to something more interesting. It's well known that if you can print
|
||||
the elements of a Haskell list, you can also print the list itself. The
|
||||
implementation looks something like this:
|
||||
|
||||
```Haskell
|
||||
instance Show a => Show [a] where
|
||||
show l = "[" ++ intercalate "," (map show l) ++ "]"
|
||||
```
|
||||
|
||||
We can't print just any list; for instance, `[Int -> Bool]` does not have
|
||||
a `Show` instance, because `Int -> Bool` itself does not have a show instance.
|
||||
This dependency is encoded in Prolog as follows:
|
||||
|
||||
{{< codelines "Prolog" "typeclass-prolog/kb.pl" 2 2 >}}
|
||||
|
||||
Implicit in both the Haskell instance and the Prolog rule is the "forall"
|
||||
quantifier: we have "for all types `a`, if `Show a`, then `Show [a]`",
|
||||
and "for all terms `X`, if `show(X)` holds, then `show(list(X))` holds".
|
||||
|
||||
With only these two instances, what types can we print? Let's ask Prolog:
|
||||
|
||||
```Prolog
|
||||
?- show(X).
|
||||
X = unit ;
|
||||
X = list(unit) ;
|
||||
X = list(list(unit)) ;
|
||||
X = list(list(list(unit))) ;
|
||||
...
|
||||
```
|
||||
|
||||
These correspond to `()` (naturally), `[()]`, `[[()]]`, and so on. Indeed,
|
||||
in Haskell, we'd be able to use `show` to turn values of these types
|
||||
(and only these types, assuming our two instances) into strings.
|
||||
|
||||
```Haskell
|
||||
show () = "()"
|
||||
show [()] = "[()]"
|
||||
show [[()],[()]] = "[[()],[()]]"
|
||||
show [[[()],[()]]] = "[[[()],[()]]]"
|
||||
...
|
||||
```
|
||||
|
||||
A similar principle applies to tuples, `(a,b)`. Here's the Haskell instance:
|
||||
|
||||
```Haskell
|
||||
instance (Show a, Show b) => Show (a, b) where
|
||||
show (a,b) = "(" ++ show a ++ "," ++ show b ++ ")"
|
||||
```
|
||||
|
||||
The Prolog encoding is pretty much as one would expect:
|
||||
|
||||
{{< codelines "Prolog" "typeclass-prolog/kb.pl" 3 3 >}}
|
||||
|
||||
### What of the superclasses?
|
||||
|
||||
We've explored few of the possibilities in the Haskell's typeclass system.
|
||||
For instance, there's another well-known typeclas, `Ord`:
|
||||
|
||||
```Haskell
|
||||
class Eq a => Ord a where
|
||||
-- ...
|
||||
```
|
||||
|
||||
This typeclass has a _superclass_: in order for some type `a` to have
|
||||
class `Ord`, it must also have class `Eq`. We haven't seen superclasses
|
||||
until now, but they are quite important to Haskell: `Applicative` is a
|
||||
superclass of `Monad`, which means you can use `liftA2` and `<*>` on any monad.
|
||||
A typeclass having a superclass has two important implications for us.
|
||||
|
||||
First, we may not put down an instance of, say, `Ord a`, if we don't
|
||||
already have an implementation of `Eq a`.
|
Loading…
Reference in New Issue
Block a user