======= Crash course into Haskell's syntax ====== ===== Haskell in the Programming Paradigms lecture ===== We use Haskell at PP in order to illustrate some functional programming concepts (or functional programming design patterns) which can be used in any functional or multi-paradigm lecture. These are: * programming with side-effect-free (or pure) functions * programming with higher-order functions * programming with Abstract (or Algebraic) Datatypes * programming with lazy evaluation With this in mind, we point out that this is not a course about the Haskell language - although we will quite a few of its particular aspects (with IO being a distinguished omission). In this page you will find the subset of programming constructs which are used during the lecture. ===== Functions in Haskell ===== Functions are defined as follows: ... = For instance: f x y = x + y Anonymous functions can be defined as follows: \ ... -> For instance: \x y -> x + y Function calls are of the form: ... for a function with **at least** n parameters. For instance: f 1 2 ==== Parentheses and $ ==== It is a good practice (**although not always necessary**) to enclose a function call in parentheses, e.g. ''(f 1 2)'' instead of ''f 1 2''. This actually makes the code more legible and avoids typing bugs. To avoid cluttering of nested function calls, e.g. ''f (g (h x y))'', the function application operator ''$'' can be used. The type of ''$'' is ''(a->b)->a->b'', that is, it takes a function and an argument and applies the latter on the function. The previous call can be rewritten as: f $ g $ h x y You can also interpret ''$'' as a means for enforcing precedence (i.e. first ''h'' is called, then ''g'' then ''f''). Functional composition is often a good alternative to ''$''. For instance, the previous function call can be written as: (f . g) (h x y) Meaning that function ''(f . g)'' is called with argument ''(h x y)''. Note that ''(f . g . h) x y'' is not equivalent, and signifies the call of ''f . g . h'' with arguments ''x'' and ''y''. ==== Infix and prefix ==== Usually (although not mandatory), functions are defined in //prefix// form (as seen in the previous examples). Some Haskell functions are infix (e.g. ''+'', ''.'' or ''$''). We can //turn// an infix function into a prefix one using parentheses. E.g. '':t ($)'' or ''(+) 1 2''. Similarly, we can //turn// an prefix function into an infix, using quasiquotes, e.g. ''1 `f` 2'' where ''f x y = x + y''. ==== where ==== Often we would like to define auxiliary functions whose scope (visibility) is limited to our function definition. We can do this using ''where''. We illustrate it on several examples: f x y = (inc x) + y where inc v = v + 1 f x y = (inc x) + (dec y) where inc v = v + 1 dec v = v - 1 f x y = (inc x) + (dec y) where inc v = (g v) + 1 where g 0 = 0 g _ = 1 dec v = v - 1 f x y = (inc x) + (dec y) where inc v = (g v) + 1 g 0 = 0 g _ = 1 dec v = v - 1 The last two examples construct functions ''f'' with the same behaviour, however they are not identical. In the latter example, the function ''g'' is visible in the entire ''where'' body, thus other functions defined in the same scope could employ it. ===== Basic datatypes ====== The basic datatypes used in the PP lecture are: * integers and floats (''Integer'' and ''Float'') * booleans (''Bool'') * char (''Char'') The container types are: * lists (''[a]'') * pairs (''(a,b)'') * functions (''a -> b'') where ''a'' and ''b'' are type variables. The base constructors for lists are: * ''[] :: [a]'' (the empty list) * ''(:) :: a -> [a] -> [a]'' (//cons//) The list observers are: * ''head :: [a] -> a'' * ''tail :: [a] -> [a]'' Other distinguished operators are: * ''(++) :: [a] -> [a] -> [a]'' (//appending two lists//) The base constructor pairs is: * ''(,) :: a -> b -> (a,b)'' Pair observers are: * ''fst :: (a,b) -> a'' * ''snd :: (a,b) -> b'' The type ''String'' in Haskell is an alias for ''[Char]''. ===== Pattern matching ====== A powerful mechanism in Haskell is pattern matching, which allows defining function body-expressions based on how a value is constructed. In a pattern, only base constructors can be used. The simplest example is: f [] = ... f (x:xs) = ... which defines two body expressions, one for an argument equal to the empty list, and one for a list constructed via the //cons// operator. Here, ''x'' is the first element of the list and ''xs'' is the rest of the list. Patterns can be combined liberally to express more complicated arguments such as: * ''(x:y:xs)'' - a list of at least two elements * ''(1:xs)'' - a list which contains ''1'' as its first element * ''[1,2]'' - the list ''[1,2]'' * ''(1:2:[])'' - same as above * ''(x,y)'' - a pair where ''x'' is the first element and ''y'' is the second * ''( (x:xs),(y:ys) )'' - a pair where both the first and the second elements are lists of at least one element * ''_'' - //anything// - or an anonymous value Pattern matching in Haskell is not limited to function definitions, but can also be used in the body expression of a function, using ''case'' expressions: case of -> ... -> The '''' is evaluated to a value, and the value is hence checked to see if it satisfies each pattern in turn. When a pattern is found, the case expression is evaluated to the pattern's respective body expression. Example: case foldr (:) [] [1,2,3] of [] -> 1 (x:xs) -> 2 (x:y:xs) -> 3 _ -> 4 In the previous example, ''foldr (:) [] [1,2,3]'' evaluates to ''[1,2,3]''. The first pattern is not satisfied, however the second one is, thus the case expression will return ''2''. The third pattern is more particular but occurs after the first satisfying pattern. ===== Guards ===== Instead of relying on how values are constructed, a programmer may want to define a function's body expression in terms of boolean conditions. This can be done using **guards**, as follows: ... | = ... | = | otherwise = Example: f x y | x + 5 > y = 1 | x == y = 2 | x == head (sort [x,y]) = 3 In the previous example, ''f 1 5'' will return ''1'' while ''f 4 5'' will return ''3''.