Skip to content

Commit

Permalink
Merge pull request #76 from JordanMartinez/development
Browse files Browse the repository at this point in the history
Make release: ps-0.12.x-v0.7.2
  • Loading branch information
JordanMartinez authored Sep 24, 2018
2 parents e3a66cd + 427ab14 commit 477d814
Show file tree
Hide file tree
Showing 31 changed files with 2,823 additions and 8 deletions.
20 changes: 20 additions & 0 deletions 21-Hello-World/04-Debugging/src/01-Type-Directed-Search.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,23 @@ main = do
```

If you encounter a problem or need help, this should be one of the first things you use.

## Getting the Type from the Compiler

This tip comes from cvlad on the Slack channel (I've edited his response below for clarity):
> If you want the type of `something`, a good trick is to assert its type to something random like `Unit`. For example, you could write: `(log "hola") :: Unit`. The compiler will give you an error such as, "Cannot unify `Unit` with `_`", where `_` will be the type of the expression.
## Getting the Type of a Function from the Compiler

There are two possible situations where this idea might help:
- You know how to write the body for a function but aren't sure what it's type signature is
- You're exploring a new unfamiliar library and are still figuring out how to piece things together. So, you attempt to write the body of a function but aren't sure what it's type signature will be.

In such cases, we can completely omit the type signature and the compiler will usually infer what it is for us:
```purescript
-- The following code is completely made up
-- no type signature here, so the compiler will output a warning
-- stating what it inferred it to be
doSomething x y = runK $ repeat 4 $ add 5 \x -> x - 3
```
5 changes: 4 additions & 1 deletion 21-Hello-World/07-Application-Structure/psc-package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
{
"name": "untitled",
"set": "psc-0.12.0",
"set": "psc-0.12.0-20180901",
"source": "https://github.com/purescript/package-sets.git",
"depends": [
"run",
"variant",
"free",
"strings",
"debug",
"psci-support",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- [RWS](https://pursuit.purescript.org/packages/purescript-transformers/4.1.0/docs/Control.Monad.RWS.Trans#t:RWST) a convenience monad that combines `ReaderT`, `WriterT`, and `StateT` into the same monad type
- [ListT](https://pursuit.purescript.org/packages/purescript-transformers/4.1.0/docs/Control.Monad.List.Trans#t:ListT), a monad that returns a `List a`. In addition, it provides the regular list functions you'd expect
- [MaybeT](https://pursuit.purescript.org/packages/purescript-transformers/4.1.0/docs/Control.Monad.Maybe.Trans#t:MaybeT), a monad the returns a `Maybe a`
- [MonadRec](https://pursuit.purescript.org/packages/purescript-tailrec/4.0.0/docs/Control.Monad.Rec.Class#t:MonadRec) (not included in the `purescript-transformers` library): guarantees stack safety for monad transformers

## Requires More Understanding

Expand Down
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.
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.
Loading

0 comments on commit 477d814

Please sign in to comment.