Skip to content

Commit

Permalink
Merge pull request #105 from JordanMartinez/development
Browse files Browse the repository at this point in the history
Make next minor release: PS-0.12.x-v0.9.2
  • Loading branch information
JordanMartinez committed Oct 19, 2018
2 parents 6366c99 + 65385ef commit 768dcc3
Show file tree
Hide file tree
Showing 10 changed files with 159 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,31 @@ mkString3 a b = case a, b of
Apple, Cherry -> "An apple and a cherry"
_, _ -> "You didn't really think I would type out all of them, did you?!?"

warning :: String
warning = """
See: https://github.com/purescript/purescript/issues/3443
Pattern Matching -> Case -> Pattern guard == compiler error
The following code will not compile:
test :: SomeType
test a
| false =
case false of
true | a > 12 -> true
| otherwise = true
To make it compile, wrap the `case _ of` syntax block in parenthesis:
test :: SomeType
test a
| false =
(case false of
true | a > 12 -> true)
| otherwise = true
"""

-- Necessary to get this file to compile
length :: String -> Int
length _ = 4
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ See the indentation rules to correctly indent your where clause
madeUpFunctions.
-}

{-
warning :: String
warning = """
WARNING!
The 'where' clause cannot be used with pattern matching due to a bug
Expand All @@ -90,7 +92,7 @@ patternMatchGuardWithWhere x | x == 0 = stringValue
stringValue :: String
stringValue = "string value"
-------------------------------------------------------------
-}
"""

-- necessary to make this file compile:
somethingThatUses :: String -> String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,15 @@ instance intZeroAppender :: ZeroAppender Int where
append = (+)
zeroValue = 0

warning_orphanInstance :: String
warning_orphanInstance = """
Be aware of what an 'orphan instance' is.
See the following link for more info:
https://github.com/purescript/documentation/blob/master/errors/OrphanInstance.md
"""

{-
Note: Type class instances that use type aliases (i.e. the `type` keyword)
wil fail to compile. The following code demonstrates this.
Expand Down
8 changes: 8 additions & 0 deletions 21-Hello-World/06-Benchmarking/ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,11 @@ pulp --psc-package run --src-path "benchmark" -m Benchmarking.Syntax.Benchotron
2. It will output a file in the freshly-created `tmp` directory
3. Upload the outputted file to [this link](http://harry.garrood.me/purescript-benchotron-svg-renderer/)
4. Download the graph as an SVG or PNG

## Generating benchmarks for real-world projects

In real-world projects, one would run this command:
```bash
pulp --psc-package run -m Performance.ModulePath.To.MainModule --src-
path benchmark --include "src:test"
```
21 changes: 12 additions & 9 deletions 21-Hello-World/08-Application-Structure/src/01-MTL/ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,15 @@ main = do
-- similar for `print` function
print five_string
```
`bind`/`>>=` insures sequential computation. Functions work with `bind` to compute a new value. When we want to group similar functions together, we call it an "effect". So what kind of "effects" (e.g. functions that work with `bind`/`>>=` to compute some new value) can we have? Let's now give some examples via the table below:
`bind`/`>>=` insures that one computation occurs before another (i.e. sequential computation), but it does not define what kinds of computation are done. Thus, we must explain what "effects" are. **Effects** are the types of computations we want to use. These effects can be grouped together into functions that define computations which `bind` executes in a sequential manner. So what kind of "effects" can we have? Let's now give some examples via the table below:

| When we want `bind` to... | ... we expect to use functions named something like ... | ... which are best abstracted together in a type class called...
| When we want a type of computation (effect) that... | ... we expect to use functions named something like ... | ... which are best abstracted together in a type class called...
| - | - | - |
| Provide for later usage a read-only value/function that may change between different program runs<br>(e.g. "settings" values; dependency injection) | <ul><li>`getSettingValue`</li><li>`getConfigValueStoredOnFile`</li><li>`getNumberOfPlayersInGame`</li></ul> | [`MonadAsk`](https://pursuit.purescript.org/packages/purescript-transformers/4.1.0/docs/Control.Monad.Reader.Class#t:MonadAsk)
| Modify the state of a data structure<br>(e.g. changing the nth value in a list)| <ul><li>`pop stack`</li><li>`replaceAt index treeOfStrings "some value"`</li><li>`(\int -> int + 1)`</li></ul> | [`MonadState`](https://pursuit.purescript.org/packages/purescript-transformers/4.1.0/docs/Control.Monad.State.Class#t:MonadState)
| Return a computation's output and additional data that is generated during the computation | <ul><li>[See this SO answer](https://stackoverflow.com/a/27651976)</li><li>[See this Reddit thread](https://www.reddit.com/r/haskell/comments/3faa02/what_are_some_real_world_uses_of_writer/)</li></ul> | [`MonadTell`](https://pursuit.purescript.org/packages/purescript-transformers/4.1.0/docs/Control.Monad.Writer.Class#t:MonadTell)
| Stop computation because of an unforeseeable error<br>(e.g. "business logic error") | -- | [`MonadThrow`](https://pursuit.purescript.org/packages/purescript-transformers/4.1.0/docs/Control.Monad.Error.Class#t:MonadThrow)
| Deal with "callback hell"<br>(e.g. [de-invert inversion of control](http://www.thev.net/PaulLiu/invert-inversion.html)) | -- | [`MonadCont`](https://pursuit.purescript.org/packages/purescript-transformers/4.1.0/docs/Control.Monad.Cont.Class#t:MonadCont)
| Provides for later usage a read-only value/function that may change between different program runs<br>(e.g. "settings" values; dependency injection) | <ul><li>`getSettingValue`</li><li>`getConfigValueStoredOnFile`</li><li>`getNumberOfPlayersInGame`</li></ul> | [`MonadAsk`](https://pursuit.purescript.org/packages/purescript-transformers/4.1.0/docs/Control.Monad.Reader.Class#t:MonadAsk)
| Modifies the state of a data structure<br>(e.g. changing the nth value in a list)| <ul><li>`pop stack`</li><li>`replaceAt index treeOfStrings "some value"`</li><li>`(\int -> int + 1)`</li></ul> | [`MonadState`](https://pursuit.purescript.org/packages/purescript-transformers/4.1.0/docs/Control.Monad.State.Class#t:MonadState)
| Returns a computation's output and additional data that is generated during the computation | <ul><li>[See this SO answer](https://stackoverflow.com/a/27651976)</li><li>[See this Reddit thread](https://www.reddit.com/r/haskell/comments/3faa02/what_are_some_real_world_uses_of_writer/)</li></ul> | [`MonadTell`](https://pursuit.purescript.org/packages/purescript-transformers/4.1.0/docs/Control.Monad.Writer.Class#t:MonadTell)
| Stops computation because of an unforeseeable error<br>(e.g. "business logic error") | -- | [`MonadThrow`](https://pursuit.purescript.org/packages/purescript-transformers/4.1.0/docs/Control.Monad.Error.Class#t:MonadThrow)
| Deals with "callback hell"<br>(e.g. [de-invert inversion of control](http://www.thev.net/PaulLiu/invert-inversion.html)) | -- | [`MonadCont`](https://pursuit.purescript.org/packages/purescript-transformers/4.1.0/docs/Control.Monad.Cont.Class#t:MonadCont)

| When we want to extend the functionality of... | ... with the ability to... | ... we can use its extension type class called...
| - | - | - |
Expand All @@ -68,7 +68,7 @@ bind :: forall a. f a -> ( a -> f b) -> f b
bind :: forall a. Box a -> ( a -> Box b ) -> Box b
bind (Box 4) (\four -> Box (show four)) == Box "5"
```
**In other words, if we use one of the above monads (e.g. `MonadState`) as our monad, we cannot use any other computational monads from above.** For some parts of our program, this is not a problem. If we only want to run a state computation, we only need to use the `MonadState` effect. However, in other parts of our program, we need to use functions from both `MonadState` and `MonadReader`. Thus, we need to "compose" one effect with another. In other words, we need "composable effects."
**In other words, if we use one of the above monads (e.g. `MonadState`) as our monad, we cannot use any other computational monads from above.** For some parts of our program, this is not a problem. If we only want to run a state computation, we only need to use the `MonadState` effect. However, in other parts of our program, we need to use functions from both `MonadState` and `MonadReader`. Thus, we need to "compose" one effect with another. In other words, we need "composable effects."

So, how do we get around this limitation?

Expand All @@ -82,6 +82,8 @@ MonadState_TargetMonad
|
MonadReader_SourceMonad
```
Thus, a **monad transformer** can "transform" one monad that does not have certain effects (e.g. state manipulation) into a version that does. In short, a monad transformer augments a base monad with additional powers.

If we want to use all of the monads above, we must ultimately create a "stack" of these monad transformations that lift one monad type somewhere in the "stack" all the way up and into the monad type at the top of the stack. Using a visual, it produces this diagram (read from bottom to top):
```
Index0_TopMonad
Expand All @@ -108,4 +110,5 @@ Index5_BottomMonad
```
This idea will be covered more when we explain what `MonadTrans` is and how it works

This approach to functional programming is called "**Monad Transformers**" or "**mtl**" (short for 'monad transformers library', referring to the original Haskell library that synthesized this discovered idea into code).
In short, "**monad transformers**" augment a base monad with additional capabilties. In Haskell, "**mtl**" is a library that provides default implementations for each monad transformer in such a way that one can compose multiple effects in any order.
In Purescript, these two ideas are combined together in the library called `purescript-transformers`.
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ instance bind :: Bind Box where
```
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`..
- 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`. Here's the [related paper](https://arxiv.org/pdf/1403.0749.pdf), which will likely make more sense once we explain how the `Free` monad works.
- 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 and includes all of the optimizations discovered by Oleg Kiselyov (as stated by someone on the Slack channel).
- 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.
Expand Down
77 changes: 77 additions & 0 deletions 31-Design-Patterns/06-Variance-of-Functors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Variance of Functors

This is a summary of [this post](https://typeclasses.com/contravariance) and [this post](https://www.schoolofhaskell.com/user/commercial/content/covariance-contravariance).

When we look a `Function`'s definition, we see that it is higher-kinded by two, that is, two types need to be defined before we can have a concrete type:
```purescript
foreign import data Function :: Type -> Type -> Type
infix ? Function as ->
-- Rather than writing `Function input output`
-- we write `input -> output`
no_sugar :: Function Input Output
syntax_sugar :: Input -> Output
generic_on_input :: forall a. a -> Output
generic_on_output :: forall b. Input -> b
generic_on_both :: forall a b. a -> b
```
A type can be a `Functor` if it is higher-kinded by one. In the below example, we specify its input type and leave the output type to be defined in `map`:
```purescript
-- (input ->)
instance a :: Functor (Function input) where
-- (input -> a) -> (input -> b)
map :: forall a b. (a -> b) -> Function input a -> Function input b
map aToB inputToA = (\input -> aToB (inputToA input))
```
However, what if we specified the output type of `Function` in the instance head and left the input type to be defined in `map`? If so, it would look like this:
```purescript
type FlippedFunc output input = (input -> output)
-- (-> output)
instance a :: Functor (FlippedFunc output) where
-- (a -> output) -> (b -> output)
map :: forall a b. (a -> b) -> FlippedFunc output a -> FlippedFunc output b
map inputA_To_InputB inputA_To_Output = -- this isn't possible to implement!
```
We cannot define an instance of `Functor` in this way because the first argument in `map` changes an `a` value to a `b` value. However, if we flipped the direction of the arrow, we could write the function's body:
```purescript
type FlippedFunc output input = (input -> output)
-- (-> output)
instance a :: SomethingElse (FlippedFunc output) where
-- (a -> output) -> (b -> output)
map_ish :: forall a b. (b -> a) -> FlippedFunc output a -> FlippedFunc output b
map_ish inputB_To_InputA inputA_To_Output =
(\inputB -> inputA_To_Output (inputB_To_InputA inputB))
```
The above type class is called [`Contravariant`](https://pursuit.purescript.org/packages/purescript-contravariant/4.0.0/docs/Data.Functor.Contravariant#t:Contravariant) and `map_ish` is called `cmap`.

## Functor Re-examined

The above two type classes, `Functor` and `Contravariant`, are the same except for the direction of the arrow in the `map`/`map_ish`'s first function argument. The former is called `Functor` instead of `Covariant` because it appears more often than the latter.

| Real Name | Purescript Name | Frequency of Appearance | Usage
| - | - | - | - |
| Covariant Functor | Functor | Very frequent | Use an `a`
| Contravariant Functor | Contravariant | Infrequent | Make an `a`

## Positive and Negative Position

However, there are actually special names for the input and output types:
> **Positive** position: the type variable is the result/output/range/codomain of the function
> **Negative** position: the type variable is the argument/input/domain of the function
Or to put it into meta-language: `negativePosition -> positivePosition`

These terms are used so that one can ultimately determine whether a given type is a Covariant or Contravariant Functor by the rules of multplication:

| position 1 | position 2 | end position
| - | - | - |
| + | + | + |
| - | - | + |
| - | + | - |
| + | - | - |
5 changes: 5 additions & 0 deletions 41-Ecosystem/Data-Types/ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,8 @@ These costs can be minimized by making data structures `lazy`, waiting to comput
## Deeper Reading

For a deeper explanation of Purely Functional Data Structures, read through Chris Okasaki's [thesis](https://www.cs.cmu.edu/~rwh/theses/okasaki.pdf) or buy his [book](https://www.amazon.com/Purely-Functional-Structures-Chris-Okasaki/dp/0521663504). Then see the answer to ["What's new in purely functional data structures since Okasaki?"](https://cstheory.stackexchange.com/questions/1539/whats-new-in-purely-functional-data-structures-since-okasaki)


## See Also

An explanation on ["higher-kinded data types"](http://reasonablypolymorphic.com/blog/higher-kinded-data/)
3 changes: 3 additions & 0 deletions 41-Ecosystem/Type-Classes/External-Explanations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# External Explanations

For a good explanation of the Comonad type class, see [this explanation](http://www.haskellforall.com/2013/02/you-could-have-invented-comonads.html)
15 changes: 15 additions & 0 deletions 41-Ecosystem/Type-Classes/Functors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Functors

As explained in `Design Patterns/Variance of Functors.md`, there are two different kinds of `Functor`s: covariant Functors and contravariant Functors.

However, one also hears about `Bifunctor`, `Profunctor`, and `Invariant`. These are just different ways of combining those different kinds of functors together:

| Name | Type Class' function name | Meaning
| - | - | - |
| [Functor](https://pursuit.purescript.org/packages/purescript-prelude/4.1.0/docs/Data.Functor#t:Functor) | `map`/ `<$>`/`<#>` | Maps 1 type with a Covariant Functor
| [Contravariant](https://pursuit.purescript.org/packages/purescript-contravariant/4.0.0/docs/Data.Functor.Contravariant) | `cmap`/`>$<`/`>#<` | Maps 1 type with a Contravariant Functor
| [Bifunctor](https://pursuit.purescript.org/packages/purescript-bifunctors/4.0.0/docs/Data.Bifunctor#t:Bifunctor) | `bimap` | Maps 1 type with **a Covariant** Functor and maps 1 other type with a Covariant Functor<br>
| [Profunctor](https://pursuit.purescript.org/packages/purescript-profunctor/4.0.0/docs/Data.Profunctor) | `dimap` | Maps 1 type with **a Contravariant Functor** and maps 1 other type with a Contravariant Functor
| [Invariant](https://pursuit.purescript.org/packages/purescript-invariant/4.1.0/docs/Data.Functor.Invariant#t:Invariant) | `imap` | Maps 1 type with either/both a Covariant Functor or/and a Contravariant Functor

See also [this Profunctor](https://typeclasses.com/profunctors) explanation

0 comments on commit 768dcc3

Please sign in to comment.