-
Notifications
You must be signed in to change notification settings - Fork 74
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #76 from JordanMartinez/development
Make release: ps-0.12.x-v0.7.2
- Loading branch information
Showing
31 changed files
with
2,823 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
121 changes: 121 additions & 0 deletions
121
...e/src/02-Free/01-What-Is-The-Free-Monad/01-What-Are-Free-SomeTypeClass-Types.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
# What Are "Free" `SomeTypeClass` Types | ||
|
||
When we first introduced type classes, we explained that they are an encapsulation of 1-4 things: | ||
1. A definition of 1 or more functions/values' type signatures | ||
2. Laws to which a concrete type's implmenetation of said type class much adhere | ||
3. Functions that a type obtains for free once the core defintion/values are implemented | ||
4. An entity that groups multiple type classes together into one | ||
|
||
Thus, `SomeTypeClass` isn't so much a 'thing' as much as it is an expectation. When we say that `f` is a `SomeTypeClass`, we are really saying that `f` has an instance that implements `SomeTypeClass`'s `specialFunction` function in such a way that it adheres to `SomeTypeClass`'s laws. As we saw from the MTL folder, even `StateT`, a newtyped function, is a `Functor` because it meets all of these requirements. | ||
|
||
However, whenever we had a type that we wanted to use in a `Functor`-like way, we needed to define its `Functor` instance before we could use it in that way. In other words, we have to write a lot of boilerplate code. What if we could grant `Functor`-like capabailities for any type without implementing such an instance? That is the idea behind "free" type classes. | ||
|
||
Essentially, a "free" `TypeClassName` is a box-like type, `Wrapper`, that grants `TypeClassName`-capabilities to some other type, `A`, by providing the necessary structure for implementing a law-abiding `TypeClassName` instance for `Wrapper`. | ||
|
||
In short, to create a "free" `SomeTypeClass`, we do 2 things: | ||
1. Translate the type class into a higher-kinded type | ||
2. Do the following for each of the type class' functions, starting with the easiest function: | ||
- Translate one type class' function into a constructor for the new type | ||
- Try to implement all required instances using the constructor | ||
- Fix problems that arise | ||
|
||
## A "Free" Monoid | ||
|
||
When we look at `Monoid`, we see this type class: | ||
```purescript | ||
class Monoid a where {- | ||
append :: a -> a -> a -- include Semigroup's function -} | ||
mempty :: a | ||
-- "hello" <> "world" == "helloworld" | ||
-- "hello" <> mempty == "hello" | ||
-- mempty <> "hello" == "hello" | ||
``` | ||
Let's follow the instructions from above: First, we'll translate the type class into a data type that can take any type: | ||
```purescript | ||
data FreeMonoid a | ||
``` | ||
Second and starting with the easier function `mempty`, we'll translate it into a constructor for `FreeMonoid`. `mempty` is easy, since it translates into a placeholder constructor: | ||
```purescript | ||
data FreeMonoid a | ||
= Mempty | ||
instance s :: Semigroup (FreeMonoid a) where | ||
append a Mempty = a | ||
append Mempty a = a | ||
instance m :: Monoid (FreeMonoid a) where | ||
mempty = Mempty | ||
``` | ||
`append` is a bit harder. We need to store a value of type `a`, so we can try this: | ||
```purescript | ||
data FreeMonoid a | ||
= Mempty | ||
| Append a | ||
``` | ||
However, if try to implement this as `(Append a1) <> (Append a2)`, we can't combine `a1` and `a2`. Rather, we need to store both an `a1` and an `a2` in a single `Append`: | ||
```purescript | ||
data FreeMonoid a | ||
= Mempty | ||
-- a1 a2 | ||
| Append a a | ||
-- since `Mempty` is our placeholder instance, we can use it | ||
-- to fill the a2's spot | ||
instance s :: Semigroup (FreeMonoid a) where | ||
append Mempty Mempty = Mempty | ||
append a Mempty = a | ||
append Mempty a = a | ||
append (Append a1 Mempty) (Append a2 Mempty) = Append a1 a2 | ||
-- Works! | ||
(Append a1 Mempty) <> (Append a2 Mempty) == Append a1 a2 | ||
-- ...well, not quite! | ||
(Append a1 a2) <> (Append a3 Mempty) == -- ??? | ||
``` | ||
Our previous solution doesn't work either. However, if the failure case above is just another append, we get something like this: | ||
`((Append a1 Mempty) <> (Append a2 Mempty)) <> (Append a3 Mempty)` | ||
Rather than defining a second `a` for `Append`, what if we nested the types together? This approach makes our code finally work: | ||
```purescript | ||
data FreeMonoid a | ||
= Mempty | ||
-- a1 Mempty / Append a | ||
| Append a (FreeMonoid a ) | ||
instance s :: Semigroup (FreeMonoid a) where | ||
append Mempty Mempty = Mempty | ||
append a Mempty = a | ||
append Mempty a = a | ||
append (Append a memptyOrAppend) otherAppend = | ||
Append a (memptyOrAppend <> otherAppend) | ||
instance m :: Monoid (FreeMonoid a) where | ||
mempty = Mempty | ||
``` | ||
The above code is the exact same thing as a familiar data type, `List`: | ||
```purescript | ||
data List a | ||
= Nil | ||
| Cons a (List a) | ||
instance s :: Semigroup (List a) where | ||
append Nil Nil = Nil | ||
append a Nil = a | ||
append Nil a = a | ||
append (Cons head tail) otherList = | ||
Cons head (tail <> otherList) | ||
instance m :: Monoid (List a) where | ||
mempty = Nil | ||
``` | ||
Thus, we say that `List` is a "free" monoid because by wrapping some type (e.g. `Fruit`) into a `List`, we get a monoid instance for `Fruit` for free: | ||
```purescript | ||
data Fruit | ||
= Apple | ||
| Orange | ||
| Banana | ||
(Cons (Apple Nil)) <> (Cons Banana Nil) == Cons (Apple (Cons Banana Nil)) | ||
``` | ||
This idea can be useful for when we have types that can't implement specific type classes. |
60 changes: 60 additions & 0 deletions
60
...e/src/02-Free/01-What-Is-The-Free-Monad/02-What-Is-And-Is-Not-The-Free-Monad.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
# What Is and Is Not The Free Monad | ||
|
||
Since `List` was the free monoid type, what would be the free monad type? | ||
```purescript | ||
-- monad class fully expanded... | ||
class Monad f where | ||
map :: forall a. (a -> b) -> f a -> f b | ||
apply :: forall a. f (a -> b) -> f a -> f b | ||
bind :: forall a. f a -> (a -> f b) -> f b | ||
pure :: forall a. a -> f a | ||
``` | ||
Using our previous instructions... | ||
> In short, to create a "free" `SomeTypeClass`, we do 3 things: | ||
> 1. Translate the type class into a higher-kinded type | ||
> 2. Do the following for each of the type class' functions, starting with the easiest function: | ||
> - Translate one type class' function into a constructor for the new type | ||
> - Try to implement all required instances using the constructor | ||
> - Fix problems that arise | ||
We'll start with the simplest function, `pure`: | ||
```purescript | ||
data FreeMonad a | ||
= Pure a | ||
instance applicative :: Applicative FreeMonad where | ||
pure a = Pure a | ||
instance functor :: Functor FreeMonad where | ||
map f (Pure a) = Pure (f a) | ||
instance apply :: Apply FreeMonad where | ||
apply (Pure f) (Pure a) = Pure (f a) | ||
instance bind :: Bind FreeMonad where | ||
bind (Pure a) f = f a | ||
``` | ||
Well, that was easy... Wasn't this the same implementation as `Box` from before? You are correct. | ||
```purescript | ||
data Box a = Box a | ||
instance applicative :: Applicative Box where | ||
pure a = Box a | ||
instance functor :: Functor Box where | ||
map f (Box a) = Box (f a) | ||
instance apply :: Apply Box where | ||
apply (Box f) (Box a) = Box (f a) | ||
instance bind :: Bind Box where | ||
bind (Box a) f = f a | ||
``` | ||
We can see here that `Box` is a free `Functor`, `Apply`, `Applicative`, `Bind`, and therefore `Monad` for any type with kind `Type`. However, that's not what others mean when they talk about **the** free `Functor`, `Applicative`, and `Monad` types. Those "free" types make any type with kind `Type -> Type` a functor, applicative, and monad. AFAIK, these types were not discovered at the same time by the same people. Rather, they were discovered over time as solutions to specific problems. See below for these types: | ||
- Coyoneda - [docs](https://pursuit.purescript.org/packages/purescript-free/5.1.0/docs/Data.Coyoneda#t:Coyoneda) & [source code](https://github.com/purescript/purescript-free/blob/v5.1.0/src/Data/Coyoneda.purs#L32) - a free `Functor` for any type of kind `Type -> Type`. | ||
- FreeAp - [docs](https://pursuit.purescript.org/packages/purescript-freeap/5.0.1/docs/Control.Applicative.Free) & [source code](https://github.com/ethul/purescript-freeap/blob/v5.0.1/src/Control/Applicative/Free.purs#L22-L25) - a free `Applicative` for any type of kind `Type -> Type`.. | ||
- Free (the original) - a free `Monad` for any `Functor`. Its implementation suffers from big performance problems when run. | ||
- Free (reflection without remorse)[docs](https://pursuit.purescript.org/packages/purescript-free/5.1.0/docs/Control.Monad.Free#t:Free) & [source code](https://github.com/purescript/purescript-free/blob/v5.1.0/src/Control/Monad/Free.purs#L37-L37) - a free `Monad` for any `Functor`. Its implementation removes the performance penalties of the original version | ||
- Freer (Functor-less Free) - a free monad for any type of kind `Type -> Type`. (the `Functor` constraint is satisfied via `Coyoneda`). There does not appear to be a Purescript library for this yet. | ||
|
||
At this time, `Coyoneda` and `FreeAp` will not be discussed in this folder. Rather, the upcoming files will focus entirely on the `Free` monad. |
Oops, something went wrong.