Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
pp:2023:haskell:l07 [2023/04/19 22:12]
tpruteanu
pp:2023:haskell:l07 [2023/04/27 01:17] (current)
mihai.udubasa [Lambda calculus as a programming language (optional)] fix typo
Line 69: Line 69:
 **A:** We can evaluate any of them, and it is guaranteed by [[https://​en.wikipedia.org/​wiki/​Church%E2%80%93Rosser_theorem | Church-Rosser theorem]] that if the expression is reducible, we will eventually get the same $ \beta $**-normal form**. **A:** We can evaluate any of them, and it is guaranteed by [[https://​en.wikipedia.org/​wiki/​Church%E2%80%93Rosser_theorem | Church-Rosser theorem]] that if the expression is reducible, we will eventually get the same $ \beta $**-normal form**.
  
-To not just randomly choose **redexes**,​ there exist // reduction strategies //, from which we will use the **Normal Order** and **Applicative Order**: \\+To not just randomly choose **redexes**,​ there exist //reduction strategies//,​ from which we will use the **Normal Order** and **Applicative Order**: \\
   * **Normal Order** evaluation consist of always reducing the //​leftmost//,​ //​outermost//​ **redex** (whenever possible, subsitute the arguments into the function body) \\   * **Normal Order** evaluation consist of always reducing the //​leftmost//,​ //​outermost//​ **redex** (whenever possible, subsitute the arguments into the function body) \\
   * **Applicative Order** evaluation consist of always reducing the //​leftmost//,​ //​innermost//​ **redex** (always reduce the function argument before the function itself) \\   * **Applicative Order** evaluation consist of always reducing the //​leftmost//,​ //​innermost//​ **redex** (always reduce the function argument before the function itself) \\
Line 82: Line 82:
   - $ \lambda x.\lambda y.(x \ y \ x) \ \lambda x.\lambda y.x \ (\lambda x.\lambda y.\lambda z.(x \ z \ y) \ \lambda x.\lambda y.y)$   - $ \lambda x.\lambda y.(x \ y \ x) \ \lambda x.\lambda y.x \ (\lambda x.\lambda y.\lambda z.(x \ z \ y) \ \lambda x.\lambda y.y)$
   - $ \lambda x.y \ (\lambda x.(x \ x) \ \lambda x.(x \ x))$   - $ \lambda x.y \ (\lambda x.(x \ x) \ \lambda x.(x \ x))$
 +
 +==== Lambda calculus as a programming language (optional) ====
 +
 +The [[https://​en.wikipedia.org/​wiki/​Church%E2%80%93Turing_thesis | Church-Turing thesis]] asserts that any //​computable//​ function can be computed using lambda calculus (or Turing Machines or equivalent models). \\
 +For the curious, a series of additional exercises covering this topic can be found here: [[pp:​2023:​haskell:​l07-extra|Lambda Calculus as a programming language]]. \\
  
 ===== 7.2 Intro to Haskell ===== ===== 7.2 Intro to Haskell =====
 +
 +**Prequisites**:​ having a working haskell environment ([[pp:​haskell-environment|Haskell Environment]])
 +
 +**Haskell** is a general-purpose,​ purely functional programming language, that we will use for the rest of the semester to showcase functional patterns and programming styles. \\
 +
 +{{ :​pp:​2023:​haskell:​haskell.png?​nolink&​200 |}}
 +
 +This section is designed for us to get comfortable with haskell syntax, we will use several concept that we learned in Scala, such as tail-recursion,​ folds and maps, but this time in a purely functional context.
 +
 +==== A trip through time ====
 +Remember: [[pp:​2023:​scala:​l01|Lab 1. Introduction to Scala]]
 +
 +**7.2.1.** Implement a tail-recursive function that computes the factorial of a natural number.
 +<code haskell>
 +fact :: Int -> Int
 +fact = undefined
 +</​code>​
 +**7.2.2.** Implement a tail-recursive function that computes the greatest common divisor of two natural numbers.
 +<code haskell>
 +mygcd :: Int -> Int -> Int
 +mygcd a b = undefined
 +</​code>​
 +**7.2.3.** Implement the function ''​mySqrt'' ​ which computes the square root of an integer $ a $.
 +
 +==== Lists ====
 +The following Scala syntax for working with lists, can be translated to Haskell as follows:
 +^ Scala ^ Haskell cases ^ Haskell pattern matching ^ Haskell guards ^
 +|<code scala>
 +def f(l: List[Int]) = l match {
 +  case Nil => ...
 +  case (x::xs) => ...
 +}
 +</​code>​|<​code haskell> ​
 +f l = case l of
 +  [] -> ...
 +  (x:xs) -> ...
 +</​code>​ | <code haskell>
 +f [] = ...
 +f (x:xs) = ...
 +</​code>​ | <code haskell>
 +f l | l == [] = ...
 +    | otherwise = ...
 +</​code>​ |
 +
 +**7.2.4.** Implement funtions ''​mymin''​ and ''​mymax''​ that take a list of ints, and return the smallest/​biggest value in the list.
 +
 +**7.2.5.** Implement a function ''​unique''​ that takes a list of ints, and removes all duplicates.
 +
 +**7.2.6.** Given a list of ints, return a list of strings where for each element, return:
 +  * **'​Fizz'​** if the number is divisible by 3
 +  * **'​Buzz'​** if the number is divisible by 5
 +  * **'​FizzBuzz'​** if the number is divisible by 3 **and** 5
 +  * a string representation of the number otherwise
 +
 +**7.2.7.** Extend the function from **7.2.6.** with the following rules:
 +  * **'​Bazz'​** if the number is divisible by 7
 +  * **'​FizzBazz'​** if the number is divisible by 21
 +  * **'​BuzzBazz'​** if the number is divisible by 35
 +  * **'​FizzBuzzBazz'​** if the number is divisible by 105
 +
 +\\
 +<​hidden>​
 +You can test **7.2.6.** and **7.2.7.** with the following snippet, if your function is $ f $:
 +<code haskell>
 +f [1..n]
 +</​code>​
 +</​hidden>​
 +
 +<​note>​
 +In Haskell, the list data type is denote by the type the list holds surrounded by square paranthesis.
 +<code haskell>
 +[Int] -- list of ints
 +[Double] -- list of doubles
 +[[Int]] -- list of lists of ints (matrices)
 +
 +[] -- !!! not a data type, represents the empty list (Nil in Scala)
 +</​code>​
 +</​note>​
 +
 +==== Types in Haskell ====
 +
 +In Haskell, functions are curried by default, **i.e.** a function:
 +<code haskell>
 +f a b = ...
 +</​code>​
 +is the same as:
 +<code haskell>
 +f = \a -> \b -> ...
 +</​code>​
 +
 +So, if $ a $ is a ''​Int''​ and $ b $ a ''​Double'',​ and $ f $ returns a ''​Char'',​ it would have the following type:
 +<code haskell>
 +f :: Int -> Double -> Char
 +</​code>​
 +
 +**7.2.8.** Check the type signature of the following functions:
 +  * ''​foldl''​
 +  * ''​foldr''​
 +  * ''​filter''​
 +  * ''​map''​
 +
 +<note important>​
 +If a function is not ambigous, ''​ghc''​ can infer the type signature, for **educational** purposes, going forward you will have to write signatures for all functions you define, this is considered good practice and helps prevent bugs.
 +</​note>​
 +
 +<note tip>
 +In ''​ghci'',​ you can check the type of a expression with: '':​t''​
 +</​note>​
 +
 +===== 7.3 Brain Twisters =====
 +
 +**7.3.1.** Implement ''​map''​ using ''​foldl''​ and ''​foldr''​.
 +<code haskell>
 +mymapl :: (a -> b) -> [a] -> [b]
 +mymapr :: (a -> b) -> [a] -> [b]
 +</​code>​
 +
 +**7.3.2.** Implement ''​filter''​ using ''​foldl''​ and ''​foldr''​.
 +<code haskell>
 +myfilterl :: (a -> Bool) -> [a] -> [a]
 +myfilterr :: (a -> Bool) -> [a] -> [a]
 +</​code>​
 +
 +**7.3.3.** Implement ''​foldl''​ using ''​foldr''​.
 +<code haskell>
 +myfoldl :: (a -> b -> a) -> a -> [b] -> a
 +</​code>​
 +
 +**7.3.4.** Implement ''​bubbleSort''​.
 +<code haskell>
 +bubbleSort :: [Int] -> [Int]
 +</​code>​
 +
 +**7.3.5.** Implement ''​quickSort''​.
 +<code haskell>
 +quickSort :: [Int] -> [Int]
 +</​code>​