paf31 / purescript-book

Sources for the PureScript book
454 stars 369 forks source link

Suggestions by @cmdv #58

Open paf31 opened 7 years ago

paf31 commented 7 years ago

Ported from the old book-sources repo:

Chapter 2

  1. Installing Tools:

Make sure you have npm installed and available on your path.

some might not know what a path is all about. Maybe telling them to check the version $ npm --v or maybe the path itself but not sure this works on all OS's $ which npm

  1. Computing Diagonals: does the file still need module Main where... at the top?

    import Math (sqrt)

potentially quickly explaining you can bring in multiple specific operators in the parentheses ie: import Math (sqrt, pow, min) or the whole lot if left out.

Chapter 3

  1. Project Goals: Worth mentioning where to find the repo with example again? It was last mentioned in chapter 1 so might not be obvious anymore.

  2. Project setup:

which is provided by the purescript-lists package which can be installed using Bower

I know it's mentioned in previous chapters but iterating how to use Bower might be helpful to those who have zero clue? bower i --save purescript-lists

  1. Type Constructors and Kinds:

    just like ill-typed values result in type errors, ill-kinded types result in kind errors

Is it worth showing what would not throw a kind error, given previous failing example?

  1. Simple Types:

    To enter a multi-line declaration in PSCi, we can use multi-line mode, by starting PSCi with the -m (or --multi-line-mode) option.

    think I remember there being a new method of switching modes when in PSCi?

  2. Quantified Types:

    > flip (\n s -> show n <> s) "Ten" 10

\ anonymous functions haven't been explained yet

  1. Displaying Address Book Entries:

    In PureScript, string concatenation uses the diamond operator (<>), instead of the plus operator like in Javascript.

I first read the example code and was confused by the (<>) spent a bit of time trying to work it out and not reading further, then noticing it's explanation was bellow! is it worth putting the explanation after first code example?

  1. Creating Address Books:

we can use the Cons function from Data.List...

I was left confused by what the Cons does maybe giving a little example of it in action might help cons 1 [2, 3, 4] = [1, 2, 3, 4]

  1. Curried Functions:

I already knew currying but this section left me a bit confused, I wasn't able to see how Currying worked with the address book, is it worth showing a simpler examples first like bellow or there are prob better examples out there 😄 :

add :: Int -> Int -> Int
add = /a -> /b -> a + b

increment :: Int -> Int
increment = add 1 
-- calling `add 1` with one value returns a function awaiting another value
-- at this point add looks like: (maybe explain lazy evaluation)
-- add = /1 -> /b -> 1 + b

-- now calling increment with a value
-- increment 2 = 3 
-- ad will look like this with the second value called:
-- add = /1 -> /2 -> 1 + 2

addFive :: Int -> Int
addFive = add 5
-- addFive 10 = 15

Chapter 4

  1. Introduction:

1.1 recursion helps to reduce the mutable state in our programs

Maybe a quick explanation of mutable state because if you are coming from OOP this might be an entirely new concept.

1.2

fact :: Int -> Int
fact 0 = 1
fact n = n * fact (n - 1)

Potentially break it down a little more to show what is actually happening when the function is recursing. Think this will be a new concept to those not used FP before, maybe something like this:

fact :: Int -> Int
fact 0 = 1
fact n = n * fact (n - 1) 
-- every time a new _n_ comes into the function 1 is taken away from _n_ and the function is run again until _n_ hits 0 then the recursion stops.

fact 5 = 120
-- (fact 5 * (fact 4 * (fact 3 * (fact 2 *(fact 1 *(fact 1 ))))))
-- (5 *      (4 *      (3 *      (2 *     (1 *     (1      ))))))
--  5 * 4 * 3 * 2 * 1 * 1 = 120
  1. Recursion on Arrays:

    else 1 + length (unsafePartial tail arr)

unsafePartial hasn't been explain at this point, I personally don't quite understand it or partial. it comes up again in chapter 6 but I still struggling with why they are used and what each do. Unsure if this needs to be explained now or later.

  1. Infix Operators:

infix 8 range as ..

I wasn't sure what 8 was in terms of the higher the number the more it overrides or is it the lower the number? in fact I've forgotten 😂

  1. Do Notation:

4.1 In the case of arrays, pure simply constructs a singleton array. In fact, we could modify our factors function to use this form, instead of using pure

the pure function threw me a little, so much like Maybe it sort of becomes a wrapper / or imbeds it's results into an Array? Guess a little more clarity as to why it is useful and another good user case for it. When i read pure, I thought that somehow what I'd created was un-pured and needed purifying!

4.2 cartesian product of two arrays

A link or something to a cartesian product, as my non mathematical background I was left stumped 😄 In all honesty I was unable to figure out the exercises without finding some solutions elsewhere, they seemed very mathematically based and you needed to understand the naming to know what it should do or expected output. I also had to just look at JS examples to get what the output should look like.

  1. Folds:

    > foldl (+) 0 (1 .. 5)

unsure if fold needs an example much like in recursion fact or even pointing out that it works like JS reduce? It might not be supper clear how it works inside. It is a little more with the show example. Kind of difficult to show a foldl, maybe:

foldl (\acc n -> acc + n) 0 [1,2,3,4,5] -- 0 being the starting accumulator 
foldl (\0  1-> 0 + 1) 0 [2,3,4,5]
foldl (\1  2-> 1 + 2) 0 [3,4,5]
foldl (\3  3-> 3 + 3) 0 [4,5]
foldl (\6  4-> 6 + 4) 0 [5]
foldl (\10  5-> 10 + 5) 0 []
-- array is empty no more recursion 10 + 5 = 15
  1. Accumulators:

    reverse xs = snoc (reverse (unsafePartial tail xs))

what is snoc? maybe getting reader to check out pursuit or quick explanation

  1. Prefer Folds to Explicit Recursion:

    6.1 the reverse example can be written as a fold

maybe another explanation of the inner workings?

reverse :: forall a. Array a -> Array a
reverse = foldr (\x xs -> xs <> [x]) []

reverse [1, 2, 3]
reverse = foldr (\3 [] -> [] <> [3]) [] -- [1, 2]
reverse = foldr (\2 [3] -> [3] <> [2]) [] -- [1]
reverse = foldr (\1 [3, 2] -> [3, 2] <> [1]) [] -- []
-- empty array return what is left  [3, 2] <> [1]
[3, 2, 1]
  1. Listing All Files:

Exercises

I worked through these at the meetup with you and I struggled a fair bit, not sure if they need more hints or a something to get them started then the reader fills in the holes?

Chapter 5

  1. Simple Patterns:

(Medium) Look up Pascal’s Rule for computing binomial coefficients. Use it to write a function which computes binomial coefficients using pattern matching.

was really difficult to find a non mathematical representation of what this meant and made it really hard to understand the exercise.

  1. Named Patterns:
sortPair :: Array Int -> Array Int
sortPair arr'@[x, y]
  | x <= y = arr'
  | otherwise = [y, x]
sortPair arr = arr

Would be really good to see a before and after example as it left me puzzled as to how the @ was bing used and the last sortPair case also used arr

sortPair :: Array Int -> Array Int
sortPair [x, y]
  | x <= y = [x, y]
  | otherwise = [y, x]
sortPair arr = arr

-- above can be converted to use the named pattern

sortPair :: Array Int -> Array Int
sortPair arr'@[x, y]
  | x <= y = arr'
  | otherwise = [y, x]
sortPair arr = arr

2.1 (Medium) Write a function fromSingleton which uses an array literal pattern to extract the sole member of a singleton array. If the array is not a singleton, your function should return a provided default value. Your function should have type forall a. a -> Array a -> a

Not sure I understood the question properly and did the bellow code, not even sure that is right.

fromSingleton :: forall a. a -> Array a -> a
fromSingleton _ [x] = x
fromSingleton x _ = x
  1. Case Expressions:

Here is an example. This function computes "longest zero suffix" of an array (the longest suffix which sums to zero)

took me a little while to understand that what sat between case <here> of was in fact what was being evaluated on each recursion. So in this example:

lzs [1, -1, -2, 3]

-- first iteration
case sum xs of                 -- sum xs = 1
           0 -> xs
           _ -> lzs (tail xs) -- tail xs = [-1, -2, 3] 

-- Second iteration
case sum xs of                 -- sum xs = 0
           0 -> xs            -- xs = [-1, -2, 3] 
           _ -> lzs (tail xs)
  1. Pattern Match Failures and Partial Functions:
import Partial.Unsafe (unsafePartial)

partialFunction :: Boolean -> Boolean
partialFunction = unsafePartial \true -> true

When I saw the code I didn't have a clue what unsafePartial was is it worth showing a compile error first then an runtime error with the explanation of unsafePartial?

  1. Algebraic Data Types:

The constructors of an algebraic data type might have the same name as the ADT itself. This is quite common, and it is important not to confuse the Point type constructor with the Point data constructor - they live in different namespaces.

is this referring to data Point = Point ? I was left a little confused with different namespaces

  1. Computing Bounding Rectangles

and uses an infix operator alias \/ to compute the union of the two bounding rectangles.

I ended up trying to find that \/ infix operator on pursuit here but I don't think this is the right operator for it's use here.

Cmdv commented 7 years ago

6.4 - Computing Bounding Rectangles

class Eq a <= Ord a where

the <= hasn't been explained in that context before, people might think it just means less than or equal to .

6._ - []()