Skip to content

Commit

Permalink
Merge pull request #158 from JordanMartinez/development
Browse files Browse the repository at this point in the history
Make next minor release: 0.9.7
  • Loading branch information
JordanMartinez authored Nov 27, 2018
2 parents 67eeed2 + cfc307f commit 93934ad
Show file tree
Hide file tree
Showing 12 changed files with 141 additions and 26 deletions.
6 changes: 6 additions & 0 deletions 00-Getting-Started/03-Other-Important-Info.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ If you decide to read the book, here are things to be aware of:
- Refer to `dwhitney` fork of the book's code, which is currently being updating to use `0.12.x` code [here](https://github.com/dwhitney/purescript-book/tree/0.12)
- Refer to the ReadMe in `ROOT_FOLDER/Hello-World/Console-Lessons/Effect-Eff-and-Aff` to understand how to translate the no-longer-used `Eff` type to `Effect` type.

## Differences From Haskell

If you're coming to PureScript with a Haskell background, be sure to consult the following resources:
- [Introduction to Purescript for Haskell Developers](http://code.adriansieber.com/adrian/adriansieber-com/src/branch/master/posts/_2018-11-01_introduction_to_purescript_for_haskell_developers/main.pdf) (pdf)
- [The Purescript Documentation Repo's "Differences from Haskell" page](https://github.com/purescript/documentation/blob/master/language/Differences-from-Haskell.md)

## Documenation

- Anytime you need to look up the documentation for a package, use [Pursuit](http://pursuit.purescript.org/). Be aware that some of the deprecated packages mentioned above are still posted there.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ The following table that shows a comparison of pure and impure functions is lice
- License: [legal code](https://creativecommons.org/licenses/by-sa/4.0/legalcode) & [legal deed](https://creativecommons.org/licenses/by-sa/4.0/)
- Changes made
- Converted idea into a table that compares pure functions with impure functions
- Further expand on "does it iteract with the real world" idea with more examples from the original work
- Further expand on "does it interact with the real world" idea with more examples from the original work

Pure functions have 3 properties, but the third (marked with `*`) is expanded to show its full weight:

Expand All @@ -21,7 +21,7 @@ Pure functions have 3 properties, but the third (marked with `*`) is expanded to
| Given an input, will it always return some output? | Always <br> (Total Functions) | `n + m` | Sometimes <br> (Partial Functions) | `4 / 0 == undefined`
| Given the same input, will it always return the same output? | Always <br> (Deterministic Functions) | `1 + 1` always equals `2` | Sometimes <br> (Non-Deterministic Functions) | `random.nextInt()`
| *Does it interact with the real world? | Never | | Sometimes | `file.getText()` |
| *Does it acces or modify program state | Never | `newList = oldList.removeElemAt(0)`<br>Original list is copied but never modified | Sometimes | `x++`<br>variable `x` is incremented by one.
| *Does it access or modify program state | Never | `newList = oldList.removeElemAt(0)`<br>Original list is copied but never modified | Sometimes | `x++`<br>variable `x` is incremented by one.
| *Does it throw exceptions? | Never | | Sometimes | `function (e) { throw Exception("error") }` |

<hr>
Expand Down
83 changes: 75 additions & 8 deletions 21-Hello-World/01-FP-Philosophical-Foundations/05-Type-Classes.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,92 @@ functionName Apple = "Apple"

Putting it differently, if `Some type` can implement some `function(s)/value(s) with a specified type signature` in such a way that the implementation adheres to `specific laws`, one can say it **has** an instance of `some CT concept/term`. Some types cannot satisfy some CT terms' conditions, others can satisfy them in only one way; and still others can satisfy them in multiple ways. Thus, one does not say "`Type X` **is** an instance of [some CT Term]." Rather, one says "`Type X` **has** an instance of [some CT Term]." To see this concept in a clearer way and using pictures, see https://www.youtube.com/watch?v=iJ7V1KXJpsE

Thus, type classes abstract general concepts into an "interface" that can be implemented by various data types. They are usually an encapsulation of 1-4 things:
Thus, type classes abstract general concepts into an "interface" that can be implemented by various data types. They are usually an encapsulation of 2-3 things:

1. (Almost Always) The definition of type signatures for a single/multiple functions/values.
1. (Always) The definition of type signatures for a single/multiple functions/values.
- Functions may be put into infix notation using aliases to make it easier to write them.
2. (Almost Always) The laws by which implementations of a type class must abide.
2. (Always) The laws by which implementations of a type class must abide.
- These laws guarantee certain properties, increasing developers' confidence that their code works as expected.
- They also help one to know how to refactor code. Given `left-hand-side == right-hand-side`, evaluating code on the left may be more expensive (memory, time, IO, etc.) than the code on the right.
- **Note: Some laws cannot always be enforced.**
3. (Frequently) The functions/values that can be derived once one implements the type class.
- Most of the power/flexibility of type classes come from the combination of the main functions/values implemented in a type class' definition and these derived functions. When a type class extends another, the type class' power increases, its flexibility decreases, and its costs increase.
- For example, `Apply` is a less powerful typeclass than `Monad` because it does not have `bind` but is more flexible than `Monad` because it can compute things in parallel.
4. (Sometimes) Something that combines multiple type classes together into one.

While it is possible for a programmer to define a type class without laws, there is a debate about whether this is a good decision. Most say classes should always have laws (i.e. be a term from Category Theory) whereas others say laws should not always be required because it enables reusing the same function name for the same concept (i.e. be 'just' an interface). The counterargument to this last point is that one usually hasn't thought through their design that deeply yet. A Reddit thread discusses some of the tradeoffs of each approach here: https://www.reddit.com/r/haskell/comments/5gospp/dont_use_typeclasses_to_define_default_values/
Some type classes (e.g. `ParentTypeClass`) combine two or more other type classes (e.g. `ChildTypeClass`). **This parent-child-like relationship is not necessarily hierarchial (type must satisfy child before parent possibility exists) but synchronous (type must satisfy both child and parent)**. This leads to the following possibilities:
- `ParentTypeClass` asserts that some type has an instance for of all its `ChildTypeClass`es.
- Some derived functions for `ParentTypeClass` are only possible if the implementation can use functions from two or more `ChildTypeClass`es
- Some implementations of `ChildTypeClass` can be done more easily / better by using functions/values from `ParentTypeClass`
- `ParentTypeClass` forces implementations for `ChildTypeClass` to satisfy additional law(s).

In other words, if a type has an instance for some class X and another class Y

## Dictionaries and Lawless Type Classes

Dictionaries are what enable a function/value to magically appear in the implementation of a function's body. Read [this article about type class 'dictionaries'](https://www.schoolofhaskell.com/user/jfischoff/instances-and-dictionaries) as there really is no better way to explain this concept.

After reading the above article, it's clear that this code...
```purescript
class ToBoolean a where
toBoolean :: a -> Boolean
unUsed :: a -> String
example :: forall a. ToBoolean a => a -> Boolean
example value = toBoolean value
```

... gets desugared to this code

```purescript
type ToBooleanDictionary a =
{ toBoolean :: a -> Boolean
, unUsed :: a -> String
}
example :: forall a. ToBooleanDictionary a -> a -> Boolean
example record value = record.toBoolean value
```

Thus, type classes provide a "convenience" of sorts: rather than forcing the developer to pass in an implementation of the function, `(a -> Boolean)`, the compiler can infer what that function's implementation is **as long as it can infer the type of `a` in `class ToBoolean a`**.

### Debate: Must Type Classes Always Be Lawful?

This understanding is crucial for understanding a debate: must type classes always have laws? The following is a summary (somewhat biased) of [this Reddit thread](https://www.reddit.com/r/haskell/comments/5gospp/dont_use_typeclasses_to_define_default_values/)

Those that say "yes" likely value the benefit of laws. Laws guarantee relationships between functions and values. In short, it's easier to understand and reason about code that uses lots of generic types (e.g. `forall a. a -> String`) if one knows that functions that operate on instances of `a` or values that provide an `a` instance adheres to certain laws.

Those that say "no" likely value the benefit of overloading a function name with different implementations. For example, what if one wanted to provide a default value of some type? Reusing the function name "default" is pretty easy to understand. However, what laws does it abide by? Without a deeper context, it's hard, if not impossible, to say.

```purescript
class Default a where
default :: a
instance regularInt :: Default Int where
default = 0
-- in case some prior definition somehow 'got it wrong'
-- one can use newtypes to deal with that
newtype SpecialInt = SpecialInt Int
instance specialInt :: Default SpecialInt where
default = SpecialInt 7
```

The counterargument from those that say "laws must be required" is: "one usually hasn't through through their design that deeply yet." As an example, is `Default Int` just a different name for `Monoid`'s `mempty`, (i.e. `0` in addition (`1 + 0 == 1` and `0 + 1 == 1`)? Is their approach to their design actually flawed because there is a "better" way and they just haven't realized it yet? Are there cases where the code would "read well" if one names a function that returns some value `default` in Context A but in Context B, the code would "read well" if one called some other function `default` but which returns a different value than the first function?

The reader is left with these question:
- Are there ever times where gaining the convenience of overloaded function names are worth the loss of lawful-reasoning?
- Does this change when adds in other factors?
- Time (e.g. cost is great short-term but sucks long-term; cost stays the same through short- and long-term)
- Business cost (e.g. cost to refactor non-lawful type classes vs cost of only making lawful type classes when overloaded functions names would have made development easier / accomplished the goal at the end of the day)
- Hobby (e.g. I'm just making a fun project that no one will ever use)
- If so, when should it be done? How would one know that it was the wrong approach and when would that likely happen?
- What problems did developer X face when sticking to Side A instead of Side B?

## Examples

Here are some examples that demonstrate the combination of the 1-4 elements from above:
Here are some examples that demonstrate the combination of the 2-3 elements from above:
- The `Eq` type class specifies a type signature for a function called `eq` and `notEq`, and laws for the two, but there are not any derived functions.
- The `Ord` type class is similar to `Eq`, but it does have derived functions.

Expand All @@ -60,5 +129,3 @@ and it uses the alias `<$>` for `map` to enable one to write `function <$> f_a`
- voidLeft
- mapFlipped
- flap

To understand how typeclasses work, read [this article about 'dictionaries'](https://www.schoolofhaskell.com/user/jfischoff/instances-and-dictionaries)
41 changes: 41 additions & 0 deletions 21-Hello-World/04-Debugging/src/01-General-Debugging.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,47 @@

The following sections are tips for debugging issues that may arise in a strongly-typed language via the compiler.

## There is currently no "Actual Type / Expected Type" distinction

In the following error...
```
Could not match type
A
with type
B
... rest of error ...
```

... one might expect `A` to be the "actual" type and `B` to be the "expected" type. However, sometimes the two are swapped, so that `A` is the "expected" type and `B` is the "actual" type. This is not desirable, but is currently how the compiler works.

Why? Because [the compiler uses a mixture of unification and type inference to check types](https://github.com/purescript/purescript/issues/3111#issuecomment-335596641). See [purescript/purescript#3399](https://github.com/purescript/purescript/issues/3399) for more information.

## Distinguishing the Difference between `{...}` and `(...)` errors

(thomashoneyman recommended I document this. These examples might be incorrect since I am not fully aware of the comment that garyb made, but the general idea still applies.)

Recall that `{ label :: Type }` is syntax sugar for `Record (label :: Type)`

So, the below error means a `Record` could not unify with some other type:
```
Could not match type
{ label :: Type }
with type
String
```

Whereas the below error means a `Record` was the correct type, but some of its label-type associations were missing.
```
Could not match type
Record (label :: Type)
with type
Record (label :: Type, forgottenLabel :: OtherType)
```

## Type Directed Search

Otherwise known as "typed holes."
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
# 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
When we first introduced type classes, we explained that they are an encapsulation of 2-3 things:
1. (always) A definition of 1 or more functions/values' type signatures
2. (always) Laws to which a concrete type's implmenetation of said type class much adhere
3. (frequently) Functions that a type obtains for free once the core defintion/values are implemented

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.
Moreover, some type classes comebine two or more type classes together

Thus, `SomeTypeClass` isn't so much a 'thing' as much as it is an expectation. We don't say that `f` **is** a `SomeTypeClass` (for it could implement it in various ways); rather, 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, can be called 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.

Expand Down
2 changes: 1 addition & 1 deletion 41-Ecosystem/Data-Types/02-Maybe.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ data Maybe a
```

| Package | Type name | "Plain English" name
| - | - | - | - | - |
| - | - | - |
| [purescript-maybe](https://pursuit.purescript.org/packages/purescript-maybe/4.0.0) | `Maybe a` | A full or empty box

| Usage | Instances' Representation
Expand Down
6 changes: 3 additions & 3 deletions 41-Ecosystem/Data-Types/04-Collections/01-List-Like.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ https://pursuit.purescript.org/packages/purescript-arrays/5.0.0/docs/Data.Array.
```

| Package | Type name | "Plain English" name |
| - | - | - | - | - |
| - | - | - |
| [purescript-arrays](https://pursuit.purescript.org/packages/purescript-arrays/5.0.0/docs/Data.Array)<br>(Utils library) | `Array a` | Immutable strict size-NOT-known-at-compile-time array

| Usage | Instances & their Usage
Expand Down Expand Up @@ -54,7 +54,7 @@ infixr 6 Cons as :
```

| Package | Type name | "Plain English" name |
| - | - | - | - | - |
| - | - | - |
| [purescript-list](https://pursuit.purescript.org/packages/purescript-lists/5.0.0) | `List a` | Immutable strict singly-linked list

| Usage | Instances & their Usage
Expand Down Expand Up @@ -82,7 +82,7 @@ infixr 6 cons as :
```

| Package | Type name | "Plain English" name |
| - | - | - | - | - |
| - | - | - |
| [purescript-list](https://pursuit.purescript.org/packages/purescript-lists/5.3.0/docs/Data.List.Lazy.Types#t:List) | `List a` | Immutable lazy singly-linked list

| Usage | Instances & their Usage
Expand Down
2 changes: 1 addition & 1 deletion 41-Ecosystem/Data-Types/04-Collections/02-Map-Like.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ data Map key value = -- implementation
```

| Package | Type name | "Plain English" name |
| - | - | - | - | - |
| - | - | - |
| [purescript-ordered-collections](https://pursuit.purescript.org/packages/purescript-ordered-collections/1.0.0/docs/Data.Map.Internal) | `Map k v` | Balanced 2-3 Tree map

https://pursuit.purescript.org/packages/purescript-ordered-collections/1.0.0/docs/Data.Set
Expand Down
2 changes: 1 addition & 1 deletion 41-Ecosystem/Data-Types/04-Collections/03-Sets.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ newtype Set a = -- implementation
```

| Package | Type name | "Plain English" name |
| - | - | - | - | - |
| - | - | - |
| [purescript-ordered-collections](https://pursuit.purescript.org/packages/purescript-ordered-collections/1.0.0/docs/Data.Set) | `Set a` | Balanced 2-3 Tree Set

## Set (Hash-based)
Expand Down
2 changes: 1 addition & 1 deletion 41-Ecosystem/Data-Types/04-Collections/04-All-Others.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ data FingerTree measurement a = -- implementation
```

| Package | Type name | "Plain English" name |
| - | - | - | - | - |
| - | - | - |
| [purescript-sequences](https://pursuit.purescript.org/packages/purescript-sequences/1.0.3/docs/Data.FingerTree) | `FingerTree v a` | General-purpose FP collections data structure<br>(can be used to implement all other collections data structures)

## Seq
Expand Down
2 changes: 1 addition & 1 deletion 41-Ecosystem/Data-Types/04-Collections/05-NonEmpty.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ infixr 5 NonEmpty as :|
```

| Package | Type name | "Plain English" name |
| - | - | - | - | - |
| - | - | - |
| [purescript-nonempty](https://pursuit.purescript.org/packages/purescript-nonempty/5.0.0/docs/Data.NonEmpty) | `NonEmpty box a` | Wrapper type that guarantees that at least one instance of `a` exists
4 changes: 2 additions & 2 deletions 41-Ecosystem/Type-Classes/Functors.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ However, one also hears about `Bifunctor`, `Profunctor`, and `Invariant`. These
| - | - | - |
| [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
| [Bifunctor](https://pursuit.purescript.org/packages/purescript-bifunctors/4.0.0/docs/Data.Bifunctor#t:Bifunctor) | `bimap` | <ul><li>1st Type: **Covariant map** (e.g. `map`)</li><li>2nd Type: **Covariant map** (e.g. `map`)</li></ul>
| [Profunctor](https://pursuit.purescript.org/packages/purescript-profunctor/4.0.0/docs/Data.Profunctor) | `dimap` | <ul><li>1st Type: **Contravariant map** (e.g. `cmap`)</li><li>2nd Type: **Covariant map** (e.g. `map`)</li></ul>
| [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 93934ad

Please sign in to comment.