Skip to content

Commit b810372

Browse files
B172 - Fixes and improvements (#21)
B172 - Fixes and improvements
1 parent db5a796 commit b810372

File tree

5 files changed

+64
-44
lines changed

5 files changed

+64
-44
lines changed

tutorials/00_intro.md

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ It might happen that you encounter a more advanced concept or piece of code that
1010

1111
## Outline
1212

13-
1. FP and Haskell environment
14-
2. Functions and basics of data types
15-
3. Structure of code and branching in FP
16-
4. Important data types, handling errors
17-
5. Advanced functions
18-
6. Type classes, general and basics
19-
7. Advanced type classes 1
20-
8. Advanced type classes 2
21-
9. Testing and documentation
22-
10. Web frameworks
23-
11. Functional reactive programming
24-
12. Debugging, benchmarking and dependent types
13+
1. [FP and Haskell environment](01_fp-env.md)
14+
2. [Functions and basics of data types](02_functions-types.md)
15+
3. [Structure of code and branching in FP](03_branching.md)
16+
4. [Important data types, handling errors](04_types-errors.md)
17+
5. [Advanced functions, typeclasses intro](05_functions-typeclasses.md)
18+
6. [IO, testing, and documentation](06_io-test-doc.md)
19+
7. [Advanced type classes 1](07_common-typeclasses-1.md)
20+
8. [Advanced type classes 2](08_common-typeclasses-2.md)
21+
9. [Web application in Haskell](09_webapp.md)
22+
10. [Frontend and FRP](10_frontend-frp.md)
23+
11. [Performance and Debugging](11_performance-debug.md)
24+
12. [GHC Extensions and Dependent Types](12_exts-deptypes.md)

tutorials/01_fp-env.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ Some might not be absolutely clear to you at the moment, some are familiar from
3535
* **Referential transparency** = expression is said to be referentially transparent if it can be replaced with its corresponding value without changing the program's behavior
3636
* **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)
3737

38+
Principles [[jdegoes](https://twitter.com/jdegoes/status/974045822424776704?s=09)]:
39+
40+
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+
3844
## Haskell - the programming language
3945

4046
[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
5662
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:
5763

5864
* [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))
6066
* [Atom with plugins](https://atom-haskell.github.io/overview/)
6167

6268
Most probably you will need following stuff:

tutorials/02_functions-types.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -495,7 +495,7 @@ factorial n = fac' n 1
495495
2. `fac' 3 1`
496496
3. `fac' 2 3`
497497
4. `fac' 1 6`
498-
5. `fac' 1 6`
498+
5. `fac' 0 6`
499499
6. `6`
500500

501501
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!

tutorials/04_types-errors.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,8 @@ Prelude T B C> C.index cstr 2
159159
In other cases you need to use encoding to encode/decode bytes to/from text:
160160
161161
```
162-
E.encodeUtf8 (T.pack "život, жизнь, lífið, ਜੀਵਨ, ,حياة")
162+
Prelude T B> import qualified Data.Text.Encoding as E
163+
Prelude T B C E> E.encodeUtf8 (T.pack "život, жизнь, lífið, ਜੀਵਨ, ,حياة")
163164
"\197\190ivot, \208\182\208\184\208\183\208\189\209\140, l\195\173fi\195\176, \224\168\156\224\169\128\224\168\181\224\168\168, ,\216\173\217\138\216\167\216\169"
164165
Prelude T B C E> x = E.encodeUtf8 (T.pack "život, жизнь, lífið, ਜੀਵਨ, ,حياة")
165166
Prelude T B C E> x
@@ -174,7 +175,7 @@ Prelude T B C E> index x 2
174175
175176
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).
176177
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:
178179
179180
```haskell
180181
{-# LANGUAGE OverloadedStrings #-}
@@ -184,13 +185,13 @@ module XY ...
184185

185186
In GHCi, the extension can be enabled using the `:set` directive:
186187

187-
```haskell
188+
```
188189
Prelude> :set -XOverloadedStrings
189190
```
190191

191192
After that, a string literal type can be inferred by its usage in the source code:
192193

193-
```haskell
194+
```
194195
Prelude> import qualified Data.Text as T
195196
Prelude T> :type "abc"
196197
"abc" :: [Char]
@@ -498,5 +499,5 @@ The homework to practice working with new types, list comprehensions, containers
498499
[containers]: https://hackage.haskell.org/package/containers
499500
[GHC]: https://www.haskell.org/ghc/
500501
[Hackage]: https://hackage.haskell.org
501-
[Hayoo!]: https://hayoo.fh-wedel.de
502+
[Hayoo]: https://hayoo.fh-wedel.de
502503
[Hoogle]: https://www.haskell.org/hoogle/

tutorials/05_functions-typeclasses.md

Lines changed: 38 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Creating new own functions or using the predefined ones from libraries is common
88

99
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/).
1010

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:
1212

1313
```haskell
1414
myFunc1 :: a -> b -> c
@@ -45,7 +45,7 @@ type PSize = Int
4545
type NoVertices = Int
4646
data Polygon = -- some representation
4747
48-
mkPolygon :: PSize -> NoVertices -> Polygon
48+
mkPolygon :: NoVertices -> PSize -> Polygon
4949
mkPolygon = -- some code to make a polygon
5050
5151
mkHexagon :: PSize -> Polygon
@@ -56,28 +56,28 @@ mkRectangle = mkPolygon 4
5656
5757
--etc.
5858
```
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:
6060

6161
```haskell
62-
generalSort :: (Something -> Something -> Ordering) -> [Something] -> [Int]
62+
generalSort :: Ord a => (a -> a -> Ordering) -> [a] -> [a]
6363
generalSort orderingFn numbers = -- use the orderingFn to sort the numbers
6464

65-
fastOrderingFn :: Something -> Something -> Ordering
65+
fastOrderingFn :: Ord a => a -> a -> Ordering
6666
fastOrderingFn = -- a fast, but not too reliable ordering algorithm
6767

68-
slowOrderingFn :: Something -> Something -> Ordering
68+
slowOrderingFn :: Ord a => a -> a -> Ordering
6969
slowOrderingFn = -- a slow, but precise ordering algorithm
7070

71-
fastSort :: [Something] -> [Something]
71+
fastSort :: Ord a => [a] -> [a]
7272
fastSort = generalSort fastOrderingFn
7373

74-
goodSort :: [Something] -> [Something]
74+
goodSort :: Ord a => [a] -> [a]
7575
goodSort = generalSort slowOrderingFn
7676
```
7777

7878
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?
7979

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`:
8181

8282
```haskell
8383
flip :: (a -> b -> c) -> b -> a -> c
@@ -108,7 +108,7 @@ fastSort :: [Something] -> [Something]
108108
fastSort numbers = generalSort numbers fastOrderingFn
109109
```
110110

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" 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:
112112

113113
```haskell
114114
notCurried :: (a, b) -> (c, d) -> e
@@ -231,17 +231,17 @@ Prelude> 7 `div` 2
231231
3
232232
Prelude> foo x y z = x * (y + z)
233233
Prelude> (5 `foo` 3) 12
234-
65
234+
75
235235
```
236236

237237
You can define own operator as you would do it with function:
238238

239239
```
240240
Prelude> (><) xs ys = reverse xs ++ reverse ys
241241
Prelude> (><) "abc" "xyz"
242-
"zyxcba"
242+
"cbazyx"
243243
Prelude> "abc" >< "xyz"
244-
"zyxcba"
244+
"cbazyx"
245245
Prelude> :info (><)
246246
(><) :: [a] -> [a] -> [a]
247247
```
@@ -354,8 +354,9 @@ GHC has an extension of [Generalized Algebraic Data Types (GADTs)](https://en.wi
354354
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.
355355

356356
```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
359360
mapFunc1 = map myFunc1
360361
mapAFunc1 = map (\x y z -> x * y + z)
361362
```
@@ -428,8 +429,8 @@ Let's make a generalized higher-order function that also takes an initial value
428429

429430
```haskell
430431
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)
433434

434435
mySum = process (+) 0
435436
myProduct = process (*) 1
@@ -438,19 +439,28 @@ myProduct = process (*) 1
438439
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!
439440

440441
```
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]
442450
```
443451

444452
The type of the initial value must be the same as the type which is returned by given function. Now we get this:
445453

446454

447455
```haskell
448456
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)
451459

452460
mySum = process (+) 0
453461
myProduct = process (*) 1
462+
463+
myToStrJoin :: (Show a) => [a] -> String
454464
myToStrJoin = process (\x str -> show x ++ str) ""
455465
```
456466

@@ -459,17 +469,20 @@ Now problem is that both `(+)` and `(*)` are commutative, but `(\x str -> show x
459469

460470
```haskell
461471
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)
464474

465475
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
468478

469479
mySum = processl (+) 0
470480
myProduct = processl (*) 1
481+
482+
myToStrJoinR :: (Show a) => [a] -> String
471483
myToStrJoinR = processr (\x str -> show x ++ str) ""
472-
myToStrJoinL = processl (\x str -> show x ++ str) ""
484+
myToStrJoinL :: (Show a) => [a] -> String
485+
myToStrJoinL = processl (\str x -> show x ++ str) ""
473486
```
474487

475488
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

Comments
 (0)