You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: tutorials/01_fp-env.md
+7-1Lines changed: 7 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -35,6 +35,12 @@ Some might not be absolutely clear to you at the moment, some are familiar from
35
35
***Referential transparency** = expression is said to be referentially transparent if it can be replaced with its corresponding value without changing the program's behavior
36
36
***Recursion** = recursion occurs when a thing is defined in terms of itself or of its type (applies for functions, for example, factorial, and for types, like tree structure)
1. Orthogonal Composability: composable blocks should address a single concern.
41
+
2. Maximum Polymorphism: data types & functions should require minimum structure necessary.
42
+
3. Maximum Deferment: defer types, decisions, effects, evaluation to the last moment.
43
+
38
44
## Haskell - the programming language
39
45
40
46
[Haskell] is a pure functional programming language with strong static typing and non-strict evaluation. It is also standardized (actual standard is [Haskell 2010] and 2020 is under development). Although it is language with academic and strong math background, it is being used in [research][haskell_research], [education][haskell_education] as well as in [industry][haskell_industry] for various projects. It was created as one common language based on many previous functional languages during the 1990s. Main language implementation is [Glasgow Haskell Compiler (GHC)][GHC], which we will use extensively in this course.
@@ -56,7 +62,7 @@ Some might not be absolutely clear to you at the moment, some are familiar from
56
62
There are several editors you may use for writing Haskell programs, most probably there is some extension for your favorite editor. We recommend one of those:
57
63
58
64
*[Vim with plugins](https://wiki.haskell.org/Vim)
59
-
*[IntelliJ IDEA with HaskForce](http://haskforce.com)
65
+
*[IntelliJ IDEA with HaskForce](http://haskforce.com) (or visit their [GitHub repo](https://github.com/carymrobbins/intellij-haskforce))
60
66
*[Atom with plugins](https://atom-haskell.github.io/overview/)
Copy file name to clipboardExpand all lines: tutorials/02_functions-types.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -495,7 +495,7 @@ factorial n = fac' n 1
495
495
2.`fac' 3 1`
496
496
3.`fac' 2 3`
497
497
4.`fac' 1 6`
498
-
5.`fac' 1 6`
498
+
5.`fac' 0 6`
499
499
6.`6`
500
500
501
501
Although Haskell's [lazy evaluation] strategy and GHC optimizations make it unnecessary to write tail-recursive functions, you should be familiar with the concept as functional programmer. With Haskell, you should more focus on the readability of your code and productivity!
Prelude T B C E> x = E.encodeUtf8 (T.pack "život, жизнь, lífið, ਜੀਵਨ, ,حياة")
165
166
Prelude T B C E> x
@@ -174,7 +175,7 @@ Prelude T B C E> index x 2
174
175
175
176
As needing to pack all string literals when using non-base string representations is cumbersome, there is a handy [GHC] language extension [OverloadedStrings](https://ocharles.org.uk/blog/posts/2014-12-17-overloaded-strings.html).
176
177
177
-
Generally, [GHC] language extensions can be enabled in the source file using pragma `LANGUAGE` as the first line in the file::
178
+
Generally, [GHC] language extensions can be enabled in the source file using pragma `LANGUAGE` as the first line in the file:
178
179
179
180
```haskell
180
181
{-# LANGUAGE OverloadedStrings #-}
@@ -184,13 +185,13 @@ module XY ...
184
185
185
186
In GHCi, the extension can be enabled using the `:set` directive:
186
187
187
-
```haskell
188
+
```
188
189
Prelude> :set -XOverloadedStrings
189
190
```
190
191
191
192
After that, a string literal type can be inferred by its usage in the source code:
192
193
193
-
```haskell
194
+
```
194
195
Prelude> import qualified Data.Text as T
195
196
Prelude T> :type "abc"
196
197
"abc" :: [Char]
@@ -498,5 +499,5 @@ The homework to practice working with new types, list comprehensions, containers
Copy file name to clipboardExpand all lines: tutorials/05_functions-typeclasses.md
+38-25Lines changed: 38 additions & 25 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -8,7 +8,7 @@ Creating new own functions or using the predefined ones from libraries is common
8
8
9
9
When we talk about "currying", in Haskell it has (almost) nothing to do with dishes or spices. A famous mathematician and logician [Haskell Curry](https://en.wikipedia.org/wiki/Haskell_Curry) (the language is named after him) developed with others technique called currying: *translating the evaluation of a function that takes multiple arguments (or a tuple of arguments) into evaluating a sequence of functions, each with a single argument*. Technically, the original author of this is [Moses Schönfinkel](https://en.wikipedia.org/wiki/Moses_Sch%C3%B6nfinkel), so sometimes you may even come across a very nice name ["Schönfinkelization"](http://www.natansh.in/2012/07/27/schonfinkelization/).
10
10
11
-
Curyying can be achieved in all functional programming languages, but Haskell is special in that *all functions are curried by default*, similarly to pure lambda calculus. Let's se how we parenthesize function types:
11
+
Currying can be achieved in all functional programming languages, but Haskell is special in that *all functions are curried by default*, similarly to pure lambda calculus. Let's se how we parenthesize function types:
12
12
13
13
```haskell
14
14
myFunc1::a->b->c
@@ -45,7 +45,7 @@ type PSize = Int
45
45
type NoVertices = Int
46
46
data Polygon = -- some representation
47
47
48
-
mkPolygon :: PSize -> NoVertices -> Polygon
48
+
mkPolygon :: NoVertices -> PSize -> Polygon
49
49
mkPolygon = -- some code to make a polygon
50
50
51
51
mkHexagon :: PSize -> Polygon
@@ -56,28 +56,28 @@ mkRectangle = mkPolygon 4
56
56
57
57
--etc.
58
58
```
59
-
Here we create *specialized* versions of polygon constructor functions by providing the `PSize`parametre. As functions can be parametres, as well, we can reify the behaviour, as well:
59
+
Here we create *specialized* versions of polygon constructor functions by providing the `PSize`parameter. As functions can be parameters, as well, we can reify the behaviour, as well:
generalSort orderingFn numbers =-- use the orderingFn to sort the numbers
64
64
65
-
fastOrderingFn::Something->Something->Ordering
65
+
fastOrderingFn::Orda=>a->a->Ordering
66
66
fastOrderingFn =-- a fast, but not too reliable ordering algorithm
67
67
68
-
slowOrderingFn::Something->Something->Ordering
68
+
slowOrderingFn::Orda=>a->a->Ordering
69
69
slowOrderingFn =-- a slow, but precise ordering algorithm
70
70
71
-
fastSort::[Something] -> [Something]
71
+
fastSort::Orda=> [a] -> [a]
72
72
fastSort = generalSort fastOrderingFn
73
73
74
-
goodSort::[Something] -> [Something]
74
+
goodSort::Orda=> [a] -> [a]
75
75
goodSort = generalSort slowOrderingFn
76
76
```
77
77
78
78
This technique is very elegant, DRY and it is a basis of a good purely functional style. Its object-oriented relatives are the [Template Method design pattern](https://en.wikipedia.org/wiki/Template_method_pattern) brother married with the [Factory Method design pattern](https://en.wikipedia.org/wiki/Factory_method_pattern) – quite some fat, bloated relatives, aren't they?
79
79
80
-
As you can see, the "parametrising" parametres must come first, so we can make a curried version of the constructor function. At the same time, the order of parametres can be switched using the `flip` function that takes its (first) two arguments in the reverse order of `f`:
80
+
As you can see, the "parametrising" parameters must come first, so we can make a curried version of the constructor function. At the same time, the order of parameters can be switched using the `flip` function that takes its (first) two arguments in the reverse order of `f`:
As we said, all functions in Haskell are curried. In case you want to make them not curried, you can use tuples to "glue" parametres together:
111
+
As we said, all functions in Haskell are curried. In case you want to make them not curried, you can use tuples to "glue" parameters together:
112
112
113
113
```haskell
114
114
notCurried:: (a, b) -> (c, d) ->e
@@ -231,17 +231,17 @@ Prelude> 7 `div` 2
231
231
3
232
232
Prelude> foo x y z = x * (y + z)
233
233
Prelude> (5 `foo` 3) 12
234
-
65
234
+
75
235
235
```
236
236
237
237
You can define own operator as you would do it with function:
238
238
239
239
```
240
240
Prelude> (><) xs ys = reverse xs ++ reverse ys
241
241
Prelude> (><) "abc" "xyz"
242
-
"zyxcba"
242
+
"cbazyx"
243
243
Prelude> "abc" >< "xyz"
244
-
"zyxcba"
244
+
"cbazyx"
245
245
Prelude> :info (><)
246
246
(><) :: [a] -> [a] -> [a]
247
247
```
@@ -354,8 +354,9 @@ GHC has an extension of [Generalized Algebraic Data Types (GADTs)](https://en.wi
354
354
An anonymous function is a function without a name. It is a Lambda abstraction and might look like this: `\x -> x + 1`. Sometimes, it is more convenient to use a lambda expression rather than giving a function a name. You should use anonymous functions only for very simple functions because it decreases readability of the code.
355
355
356
356
```haskell
357
-
myFunc1 = (\x y z -> x * y + z)
358
-
myFunc2 x y z = x * y + z
357
+
myFunc1 x y z = x * y + z -- <= just syntactic sugar!
358
+
myFunc2 = (\x y z -> x * y + z) -- <= still syntactic sugar!
359
+
myFunc3 = (\x ->\y ->\z -> x * y + z) -- <= desugarized function
359
360
mapFunc1 =map myFunc1
360
361
mapAFunc1 =map (\x y z -> x * y + z)
361
362
```
@@ -428,8 +429,8 @@ Let's make a generalized higher-order function that also takes an initial value
428
429
429
430
```haskell
430
431
process:: (a->a->a) ->a-> [a] ->a
431
-
process _ initValue []= initValue
432
-
process f _ (x:xs) = f x (process xs)
432
+
process _ initValue []= initValue
433
+
process f initValue (x:xs) = f x (process f initValue xs)
433
434
434
435
mySum = process (+)0
435
436
myProduct = process (*)1
@@ -438,19 +439,28 @@ myProduct = process (*) 1
438
439
But here we are getting into a problem. Both `(+)` and `(*)` use operands and result of the same type - if we want to convert a number to string and join it in one go with `process`, it is not possible!
439
440
440
441
```
441
-
Prelude> process (\x str -> show x ++ str) "" [1,2,3,4]
442
+
*Main> process (\x str -> show x ++ str) "" [1,2,3,4]
443
+
444
+
<interactive>:18:39: error:
445
+
• No instance for (Num [Char]) arising from the literal ‘1’
446
+
• In the expression: 1
447
+
In the third argument of ‘process’, namely ‘[1, 2, 3, 4]’
448
+
In the expression:
449
+
process (\ x str -> show x ++ str) "" [1, 2, 3, 4]
442
450
```
443
451
444
452
The type of the initial value must be the same as the type which is returned by given function. Now we get this:
445
453
446
454
447
455
```haskell
448
456
process:: (a->b->b) ->b-> [a] ->b
449
-
process _ initValue []= initValue
450
-
process f _ (x:xs) = f x (process xs)
457
+
process _ initValue []= initValue
458
+
process f initValue (x:xs) = f x (process f initValue xs)
451
459
452
460
mySum = process (+)0
453
461
myProduct = process (*)1
462
+
463
+
myToStrJoin:: (Showa) => [a] ->String
454
464
myToStrJoin = process (\x str ->show x ++ str) ""
455
465
```
456
466
@@ -459,17 +469,20 @@ Now problem is that both `(+)` and `(*)` are commutative, but `(\x str -> show x
459
469
460
470
```haskell
461
471
processr:: (a->b->b) ->b-> [a] ->b-- "accumulates" in the RIGHT operand
462
-
processr _ initValue []= initValue
463
-
processr f _ (x:xs) = f x (processl xs)
472
+
processr _ initValue []= initValue
473
+
processr f initValue (x:xs) = f x (processr f initValue xs)
464
474
465
475
processl:: (b->a->b) ->b-> [a] ->b-- "accumulates" in the LEFT operand
466
-
processl _ initValue []= initValue
467
-
processl f _ (x:xs) = f (processr xs) x
476
+
processl _ initValue []= initValue
477
+
processl f initValue (x:xs) = f (processl f initValue xs) x
468
478
469
479
mySum = processl (+)0
470
480
myProduct = processl (*)1
481
+
482
+
myToStrJoinR:: (Showa) => [a] ->String
471
483
myToStrJoinR = processr (\x str ->show x ++ str) ""
472
-
myToStrJoinL = processl (\x str ->show x ++ str) ""
484
+
myToStrJoinL:: (Showa) => [a] ->String
485
+
myToStrJoinL = processl (\str x ->show x ++ str) ""
473
486
```
474
487
475
488
This is something so generally useful, that it is prepared for you and not just for lists but for every instance of typeclass `Foldable` - two basic folds `foldl`/`foldr` and related `scanl`/`scanr`, which capture intermediate values in a list:
0 commit comments