Skip to content

Commit

Permalink
B172 - Fixes and improvements (#21)
Browse files Browse the repository at this point in the history
B172 - Fixes and improvements
  • Loading branch information
MarekSuchanek committed Jul 12, 2018
1 parent db5a796 commit b810372
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 44 deletions.
24 changes: 12 additions & 12 deletions tutorials/00_intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ It might happen that you encounter a more advanced concept or piece of code that

## Outline

1. FP and Haskell environment
2. Functions and basics of data types
3. Structure of code and branching in FP
4. Important data types, handling errors
5. Advanced functions
6. Type classes, general and basics
7. Advanced type classes 1
8. Advanced type classes 2
9. Testing and documentation
10. Web frameworks
11. Functional reactive programming
12. Debugging, benchmarking and dependent types
1. [FP and Haskell environment](01_fp-env.md)
2. [Functions and basics of data types](02_functions-types.md)
3. [Structure of code and branching in FP](03_branching.md)
4. [Important data types, handling errors](04_types-errors.md)
5. [Advanced functions, typeclasses intro](05_functions-typeclasses.md)
6. [IO, testing, and documentation](06_io-test-doc.md)
7. [Advanced type classes 1](07_common-typeclasses-1.md)
8. [Advanced type classes 2](08_common-typeclasses-2.md)
9. [Web application in Haskell](09_webapp.md)
10. [Frontend and FRP](10_frontend-frp.md)
11. [Performance and Debugging](11_performance-debug.md)
12. [GHC Extensions and Dependent Types](12_exts-deptypes.md)
8 changes: 7 additions & 1 deletion tutorials/01_fp-env.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ Some might not be absolutely clear to you at the moment, some are familiar from
* **Referential transparency** = expression is said to be referentially transparent if it can be replaced with its corresponding value without changing the program's behavior
* **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)

Principles [[jdegoes](https://twitter.com/jdegoes/status/974045822424776704?s=09)]:

1. Orthogonal Composability: composable blocks should address a single concern.
2. Maximum Polymorphism: data types & functions should require minimum structure necessary.
3. Maximum Deferment: defer types, decisions, effects, evaluation to the last moment.

## Haskell - the programming language

[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.
Expand All @@ -56,7 +62,7 @@ Some might not be absolutely clear to you at the moment, some are familiar from
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:

* [Vim with plugins](https://wiki.haskell.org/Vim)
* [IntelliJ IDEA with HaskForce](http://haskforce.com)
* [IntelliJ IDEA with HaskForce](http://haskforce.com) (or visit their [GitHub repo](https://github.com/carymrobbins/intellij-haskforce))
* [Atom with plugins](https://atom-haskell.github.io/overview/)

Most probably you will need following stuff:
Expand Down
2 changes: 1 addition & 1 deletion tutorials/02_functions-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,7 @@ factorial n = fac' n 1
2. `fac' 3 1`
3. `fac' 2 3`
4. `fac' 1 6`
5. `fac' 1 6`
5. `fac' 0 6`
6. `6`

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!
Expand Down
11 changes: 6 additions & 5 deletions tutorials/04_types-errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,8 @@ Prelude T B C> C.index cstr 2
In other cases you need to use encoding to encode/decode bytes to/from text:
```
E.encodeUtf8 (T.pack "život, жизнь, lífið, ਜੀਵਨ, ,حياة")
Prelude T B> import qualified Data.Text.Encoding as E
Prelude T B C E> E.encodeUtf8 (T.pack "život, жизнь, lífið, ਜੀਵਨ, ,حياة")
"\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"
Prelude T B C E> x = E.encodeUtf8 (T.pack "život, жизнь, lífið, ਜੀਵਨ, ,حياة")
Prelude T B C E> x
Expand All @@ -174,7 +175,7 @@ Prelude T B C E> index x 2
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).
Generally, [GHC] language extensions can be enabled in the source file using pragma `LANGUAGE` as the first line in the file::
Generally, [GHC] language extensions can be enabled in the source file using pragma `LANGUAGE` as the first line in the file:
```haskell
{-# LANGUAGE OverloadedStrings #-}
Expand All @@ -184,13 +185,13 @@ module XY ...

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

```haskell
```
Prelude> :set -XOverloadedStrings
```

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

```haskell
```
Prelude> import qualified Data.Text as T
Prelude T> :type "abc"
"abc" :: [Char]
Expand Down Expand Up @@ -498,5 +499,5 @@ The homework to practice working with new types, list comprehensions, containers
[containers]: https://hackage.haskell.org/package/containers
[GHC]: https://www.haskell.org/ghc/
[Hackage]: https://hackage.haskell.org
[Hayoo!]: https://hayoo.fh-wedel.de
[Hayoo]: https://hayoo.fh-wedel.de
[Hoogle]: https://www.haskell.org/hoogle/
63 changes: 38 additions & 25 deletions tutorials/05_functions-typeclasses.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Creating new own functions or using the predefined ones from libraries is common

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

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

```haskell
myFunc1 :: a -> b -> c
Expand Down Expand Up @@ -45,7 +45,7 @@ type PSize = Int
type NoVertices = Int
data Polygon = -- some representation
mkPolygon :: PSize -> NoVertices -> Polygon
mkPolygon :: NoVertices -> PSize -> Polygon
mkPolygon = -- some code to make a polygon
mkHexagon :: PSize -> Polygon
Expand All @@ -56,28 +56,28 @@ mkRectangle = mkPolygon 4
--etc.
```
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:
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:

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

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

slowOrderingFn :: Something -> Something -> Ordering
slowOrderingFn :: Ord a => a -> a -> Ordering
slowOrderingFn = -- a slow, but precise ordering algorithm

fastSort :: [Something] -> [Something]
fastSort :: Ord a => [a] -> [a]
fastSort = generalSort fastOrderingFn

goodSort :: [Something] -> [Something]
goodSort :: Ord a => [a] -> [a]
goodSort = generalSort slowOrderingFn
```

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?

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

```haskell
flip :: (a -> b -> c) -> b -> a -> c
Expand Down Expand Up @@ -108,7 +108,7 @@ fastSort :: [Something] -> [Something]
fastSort numbers = generalSort numbers fastOrderingFn
```

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

```haskell
notCurried :: (a, b) -> (c, d) -> e
Expand Down Expand Up @@ -231,17 +231,17 @@ Prelude> 7 `div` 2
3
Prelude> foo x y z = x * (y + z)
Prelude> (5 `foo` 3) 12
65
75
```

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

```
Prelude> (><) xs ys = reverse xs ++ reverse ys
Prelude> (><) "abc" "xyz"
"zyxcba"
"cbazyx"
Prelude> "abc" >< "xyz"
"zyxcba"
"cbazyx"
Prelude> :info (><)
(><) :: [a] -> [a] -> [a]
```
Expand Down Expand Up @@ -354,8 +354,9 @@ GHC has an extension of [Generalized Algebraic Data Types (GADTs)](https://en.wi
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.

```haskell
myFunc1 = (\x y z -> x * y + z)
myFunc2 x y z = x * y + z
myFunc1 x y z = x * y + z -- <= just syntactic sugar!
myFunc2 = (\x y z -> x * y + z) -- <= still syntactic sugar!
myFunc3 = (\x -> \y -> \z -> x * y + z) -- <= desugarized function
mapFunc1 = map myFunc1
mapAFunc1 = map (\x y z -> x * y + z)
```
Expand Down Expand Up @@ -428,8 +429,8 @@ Let's make a generalized higher-order function that also takes an initial value

```haskell
process :: (a -> a -> a) -> a -> [a] -> a
process _ initValue [] = initValue
process f _ (x:xs) = f x (process xs)
process _ initValue [] = initValue
process f initValue (x:xs) = f x (process f initValue xs)

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

```
Prelude> process (\x str -> show x ++ str) "" [1,2,3,4]
*Main> process (\x str -> show x ++ str) "" [1,2,3,4]
<interactive>:18:39: error:
• No instance for (Num [Char]) arising from the literal ‘1’
• In the expression: 1
In the third argument of ‘process’, namely ‘[1, 2, 3, 4]’
In the expression:
process (\ x str -> show x ++ str) "" [1, 2, 3, 4]
```

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


```haskell
process :: (a -> b -> b) -> b -> [a] -> b
process _ initValue [] = initValue
process f _ (x:xs) = f x (process xs)
process _ initValue [] = initValue
process f initValue (x:xs) = f x (process f initValue xs)

mySum = process (+) 0
myProduct = process (*) 1

myToStrJoin :: (Show a) => [a] -> String
myToStrJoin = process (\x str -> show x ++ str) ""
```

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

```haskell
processr :: (a -> b -> b) -> b -> [a] -> b -- "accumulates" in the RIGHT operand
processr _ initValue [] = initValue
processr f _ (x:xs) = f x (processl xs)
processr _ initValue [] = initValue
processr f initValue (x:xs) = f x (processr f initValue xs)

processl :: (b -> a -> b) -> b -> [a] -> b -- "accumulates" in the LEFT operand
processl _ initValue [] = initValue
processl f _ (x:xs) = f (processr xs) x
processl _ initValue [] = initValue
processl f initValue (x:xs) = f (processl f initValue xs) x

mySum = processl (+) 0
myProduct = processl (*) 1

myToStrJoinR :: (Show a) => [a] -> String
myToStrJoinR = processr (\x str -> show x ++ str) ""
myToStrJoinL = processl (\x str -> show x ++ str) ""
myToStrJoinL :: (Show a) => [a] -> String
myToStrJoinL = processl (\str x -> show x ++ str) ""
```

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:
Expand Down

0 comments on commit b810372

Please sign in to comment.