Mercurial > repos > other > SevenLanguagesInSevenWeeks
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 |
rev | line source |
---|---|
87 | 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. |
2 | |
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 "++". | |
4 | |
5 Equality is tested with "==" (equals) and "/=" (not equals - not "is divisible by"!). Haskell is strongly typed | |
6 | |
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) | |
8 | |
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). | |
10 ":t val" lets you see the type of a variable or value, and ":set +t" lets you see the type of returned values. | |
11 | |
12 In code, functions are defined as "type function_name param = body". In code, that is always prefixed with "let". | |
13 Functions return the result of their last instruction. | |
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". | |
15 Code in files needs to be in a module, which starts "module ModuleName where" on the top line. | |
16 You can then do `:load filename.hs` in the console. This then puts you in that module on the CLI (the prompt changes). | |
17 | |
18 Haskell functions can use parameter matching on arguments - see factorial.hs. | |
19 | |
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). | |
21 | |
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.: | |
23 size [] = 0 | |
24 size (h:t) = 1 + size t | |
25 | |
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). | |
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. | |
28 | |
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". | |
30 You can also assign multiple independent variables, possibly from the same source. For example: | |
31 let crew = ["Kirk", "Spock", "McCoy"] | |
32 [(a,b) | a <- crew, b <- crew] | |
33 calculates all combinations of crew names. Changing it to: | |
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. |