annotate 7-Haskell/README.txt @ 89:7e4afb129bef

Add initial Day 2 notes with functions, partially applied, and currying
author IBBoard <dev@ibboard.co.uk>
date Sat, 15 Jun 2019 21:07:27 +0100
parents 2b5341fc4555
children eb868f089bd1
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
87
2b5341fc4555 Add Haskell Day 1 code and notes
IBBoard <dev@ibboard.co.uk>
parents:
diff changeset
1 Install with `zypper install ghc` for the Glasgow Haskell Compiler. The interactive shell (REPL) is `ghci`. Currently on v8.6.5 (was 6.12.1 in the book). Haskell uses strong static types with inference.
2b5341fc4555 Add Haskell Day 1 code and notes
IBBoard <dev@ibboard.co.uk>
parents:
diff changeset
2
2b5341fc4555 Add Haskell Day 1 code and notes
IBBoard <dev@ibboard.co.uk>
parents:
diff changeset
3 Numbers behave like numbers. Strings have double-quotes and characters have single quotes. An array of characters (in square brackets) is a string. "+" is purely numeric addition - concatenation is "++".
2b5341fc4555 Add Haskell Day 1 code and notes
IBBoard <dev@ibboard.co.uk>
parents:
diff changeset
4
2b5341fc4555 Add Haskell Day 1 code and notes
IBBoard <dev@ibboard.co.uk>
parents:
diff changeset
5 Equality is tested with "==" (equals) and "/=" (not equals - not "is divisible by"!). Haskell is strongly typed
2b5341fc4555 Add Haskell Day 1 code and notes
IBBoard <dev@ibboard.co.uk>
parents:
diff changeset
6
2b5341fc4555 Add Haskell Day 1 code and notes
IBBoard <dev@ibboard.co.uk>
parents:
diff changeset
7 Indentation is significant in Haskell (like Python) BUT you can do an "if … then … else …" on a single line (however, it will assume that "if then" is a parse error, probably because functional code isn't imperative and so can't miss the else)
2b5341fc4555 Add Haskell Day 1 code and notes
IBBoard <dev@ibboard.co.uk>
parents:
diff changeset
8
2b5341fc4555 Add Haskell Day 1 code and notes
IBBoard <dev@ibboard.co.uk>
parents:
diff changeset
9 Using functions on the wrong type of arguments seems to give helpful error messages along the lines of "No instance for (arg types) arising from a use of 'function' at X" (and "+" is a function).
2b5341fc4555 Add Haskell Day 1 code and notes
IBBoard <dev@ibboard.co.uk>
parents:
diff changeset
10 ":t val" lets you see the type of a variable or value, and ":set +t" lets you see the type of returned values.
2b5341fc4555 Add Haskell Day 1 code and notes
IBBoard <dev@ibboard.co.uk>
parents:
diff changeset
11
2b5341fc4555 Add Haskell Day 1 code and notes
IBBoard <dev@ibboard.co.uk>
parents:
diff changeset
12 In code, functions are defined as "type function_name param = body". In code, that is always prefixed with "let".
2b5341fc4555 Add Haskell Day 1 code and notes
IBBoard <dev@ibboard.co.uk>
parents:
diff changeset
13 Functions return the result of their last instruction.
2b5341fc4555 Add Haskell Day 1 code and notes
IBBoard <dev@ibboard.co.uk>
parents:
diff changeset
14 Full function definitions are preceded by a type definition line in the form "function_name :: type(s) -> type(s)". You can also have generics and have "function_name :: (Parent_class generic_name) => generic_name -> generic_name".
2b5341fc4555 Add Haskell Day 1 code and notes
IBBoard <dev@ibboard.co.uk>
parents:
diff changeset
15 Code in files needs to be in a module, which starts "module ModuleName where" on the top line.
2b5341fc4555 Add Haskell Day 1 code and notes
IBBoard <dev@ibboard.co.uk>
parents:
diff changeset
16 You can then do `:load filename.hs` in the console. This then puts you in that module on the CLI (the prompt changes).
2b5341fc4555 Add Haskell Day 1 code and notes
IBBoard <dev@ibboard.co.uk>
parents:
diff changeset
17
2b5341fc4555 Add Haskell Day 1 code and notes
IBBoard <dev@ibboard.co.uk>
parents:
diff changeset
18 Haskell functions can use parameter matching on arguments - see factorial.hs.
2b5341fc4555 Add Haskell Day 1 code and notes
IBBoard <dev@ibboard.co.uk>
parents:
diff changeset
19
2b5341fc4555 Add Haskell Day 1 code and notes
IBBoard <dev@ibboard.co.uk>
parents:
diff changeset
20 Invoking parameters doesn't use brackets (because they're used for tuples - except when it's just a single value, when they're ignored).
2b5341fc4555 Add Haskell Day 1 code and notes
IBBoard <dev@ibboard.co.uk>
parents:
diff changeset
21
2b5341fc4555 Add Haskell Day 1 code and notes
IBBoard <dev@ibboard.co.uk>
parents:
diff changeset
22 Lists can be broken up with "head" and "tail" functions (or "fst" and "snd" for tuples) *or* they can be broken up on assignment. It's like Python multi-assignment, but with colon - "(_head:_tail)" (":" is the list construction operator). It can then be used in function definitions, e.g.:
2b5341fc4555 Add Haskell Day 1 code and notes
IBBoard <dev@ibboard.co.uk>
parents:
diff changeset
23 size [] = 0
2b5341fc4555 Add Haskell Day 1 code and notes
IBBoard <dev@ibboard.co.uk>
parents:
diff changeset
24 size (h:t) = 1 + size t
2b5341fc4555 Add Haskell Day 1 code and notes
IBBoard <dev@ibboard.co.uk>
parents:
diff changeset
25
2b5341fc4555 Add Haskell Day 1 code and notes
IBBoard <dev@ibboard.co.uk>
parents:
diff changeset
26 Haskell can create ranges with [start..end]. Specifying an invalid range (e.g. decreasing) gives an empty list. Specifying an increment is different to all other languages: [start, next..end] (e.g. "[10, 8..4]" gives "[10, 8, 6, 4]" and it works out to decrement by 2).
2b5341fc4555 Add Haskell Day 1 code and notes
IBBoard <dev@ibboard.co.uk>
parents:
diff changeset
27 You can also do (lazy) infinite ranges by not specifying an end and then using "take" functions etc to pull just the values you need.
2b5341fc4555 Add Haskell Day 1 code and notes
IBBoard <dev@ibboard.co.uk>
parents:
diff changeset
28
2b5341fc4555 Add Haskell Day 1 code and notes
IBBoard <dev@ibboard.co.uk>
parents:
diff changeset
29 List comprehension is slightly pythonic: "[expr | val <- [vals] ]" where "expr" is the expression to calculate in the new list, val is a variable used in expr and [vals] is the source list. It's effectively "expression for value in list".
2b5341fc4555 Add Haskell Day 1 code and notes
IBBoard <dev@ibboard.co.uk>
parents:
diff changeset
30 You can also assign multiple independent variables, possibly from the same source. For example:
2b5341fc4555 Add Haskell Day 1 code and notes
IBBoard <dev@ibboard.co.uk>
parents:
diff changeset
31 let crew = ["Kirk", "Spock", "McCoy"]
2b5341fc4555 Add Haskell Day 1 code and notes
IBBoard <dev@ibboard.co.uk>
parents:
diff changeset
32 [(a,b) | a <- crew, b <- crew]
2b5341fc4555 Add Haskell Day 1 code and notes
IBBoard <dev@ibboard.co.uk>
parents:
diff changeset
33 calculates all combinations of crew names. Changing it to:
2b5341fc4555 Add Haskell Day 1 code and notes
IBBoard <dev@ibboard.co.uk>
parents:
diff changeset
34 [(a,b) | a <- crew, b <- crew, a /= b]
89
7e4afb129bef Add initial Day 2 notes with functions, partially applied, and currying
IBBoard <dev@ibboard.co.uk>
parents: 87
diff changeset
35 lets you add filtering to stop people being paired with themselves. Or you could use "a < b" to make it return unordered unique pairings.
7e4afb129bef Add initial Day 2 notes with functions, partially applied, and currying
IBBoard <dev@ibboard.co.uk>
parents: 87
diff changeset
36
7e4afb129bef Add initial Day 2 notes with functions, partially applied, and currying
IBBoard <dev@ibboard.co.uk>
parents: 87
diff changeset
37 Haskell has "map" - "map func list" - where "func" can be an anonymous function.
7e4afb129bef Add initial Day 2 notes with functions, partially applied, and currying
IBBoard <dev@ibboard.co.uk>
parents: 87
diff changeset
38
7e4afb129bef Add initial Day 2 notes with functions, partially applied, and currying
IBBoard <dev@ibboard.co.uk>
parents: 87
diff changeset
39 Anonymous functions are defined as:
7e4afb129bef Add initial Day 2 notes with functions, partially applied, and currying
IBBoard <dev@ibboard.co.uk>
parents: 87
diff changeset
40 (\param_1 … param_n -> function_body)
7e4afb129bef Add initial Day 2 notes with functions, partially applied, and currying
IBBoard <dev@ibboard.co.uk>
parents: 87
diff changeset
41 They can be called in-line, which looks odd:
7e4afb129bef Add initial Day 2 notes with functions, partially applied, and currying
IBBoard <dev@ibboard.co.uk>
parents: 87
diff changeset
42 (\x -> x) "Logical."
7e4afb129bef Add initial Day 2 notes with functions, partially applied, and currying
IBBoard <dev@ibboard.co.uk>
parents: 87
diff changeset
43 return "Logical." (because the anon function returns the value passed to it).
7e4afb129bef Add initial Day 2 notes with functions, partially applied, and currying
IBBoard <dev@ibboard.co.uk>
parents: 87
diff changeset
44
7e4afb129bef Add initial Day 2 notes with functions, partially applied, and currying
IBBoard <dev@ibboard.co.uk>
parents: 87
diff changeset
45 Alternatively, anonymous functions can be written as locally scoped functions after a main function definition, using "where":
7e4afb129bef Add initial Day 2 notes with functions, partially applied, and currying
IBBoard <dev@ibboard.co.uk>
parents: 87
diff changeset
46 squareAll list = map square list
7e4afb129bef Add initial Day 2 notes with functions, partially applied, and currying
IBBoard <dev@ibboard.co.uk>
parents: 87
diff changeset
47 where square x = x * x
7e4afb129bef Add initial Day 2 notes with functions, partially applied, and currying
IBBoard <dev@ibboard.co.uk>
parents: 87
diff changeset
48
7e4afb129bef Add initial Day 2 notes with functions, partially applied, and currying
IBBoard <dev@ibboard.co.uk>
parents: 87
diff changeset
49 Map can also be used with part of a function such as "(+ 1)". The book calls this a "section", but it isn't clear what it means. The wiki says a "section" is a partially applied infix operator (https://wiki.haskell.org/Section_of_an_infix_operator).
7e4afb129bef Add initial Day 2 notes with functions, partially applied, and currying
IBBoard <dev@ibboard.co.uk>
parents: 87
diff changeset
50
7e4afb129bef Add initial Day 2 notes with functions, partially applied, and currying
IBBoard <dev@ibboard.co.uk>
parents: 87
diff changeset
51 Haskell also has the standard functional filter (takes two parameter: a boolean "keep in list" function and a list) and foldl/foldr (take three parameters - a two-value function (value and accumulator), a starting value and a list).
7e4afb129bef Add initial Day 2 notes with functions, partially applied, and currying
IBBoard <dev@ibboard.co.uk>
parents: 87
diff changeset
52 You can even "foldl (+) 0 [1..3]" to sum by using "+" as the two-parameter folding function.
7e4afb129bef Add initial Day 2 notes with functions, partially applied, and currying
IBBoard <dev@ibboard.co.uk>
parents: 87
diff changeset
53
7e4afb129bef Add initial Day 2 notes with functions, partially applied, and currying
IBBoard <dev@ibboard.co.uk>
parents: 87
diff changeset
54 All of this has apparently been a lie, though. Haskell functions only have one argument! If you check the type of a multi-argument method then you get "arg_1_type -> arg_2_type -> arg_3_type -> result" rather than just "arg_1_type, arg_2_type, arg_3_type -> result" (Note: it won't be bracketed on the left because it's not expecting a three-tuple)
7e4afb129bef Add initial Day 2 notes with functions, partially applied, and currying
IBBoard <dev@ibboard.co.uk>
parents: 87
diff changeset
55 This world view can be made more apparent with the following:
7e4afb129bef Add initial Day 2 notes with functions, partially applied, and currying
IBBoard <dev@ibboard.co.uk>
parents: 87
diff changeset
56 let prod x y = x * y
7e4afb129bef Add initial Day 2 notes with functions, partially applied, and currying
IBBoard <dev@ibboard.co.uk>
parents: 87
diff changeset
57 let double = prod 2
7e4afb129bef Add initial Day 2 notes with functions, partially applied, and currying
IBBoard <dev@ibboard.co.uk>
parents: 87
diff changeset
58 let triple = prod 3
7e4afb129bef Add initial Day 2 notes with functions, partially applied, and currying
IBBoard <dev@ibboard.co.uk>
parents: 87
diff changeset
59 double and triple are partially applied functions, and the only (sane) way to be able to do that is if "prod x y" is a two-part function! The book says "When Haskell computes prod 2 4, it is really computing "(prod 2) 4". [insert anonymous functions]". This is called currying.