Differences
This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
pp:syntax [2020/03/12 18:29] pdmatei |
pp:syntax [2020/03/12 23:18] (current) pdmatei |
||
---|---|---|---|
Line 41: | Line 41: | ||
<code haskell> f 1 2 </code> | <code haskell> f 1 2 </code> | ||
+ | ==== 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 | 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. | the code more legible and avoids typing bugs. | ||
Line 54: | Line 55: | ||
(f . g) (h x y) | (f . g) (h x y) | ||
</code> | </code> | ||
- | 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'' | + | 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: | ||
+ | |||
+ | <code haskell> | ||
+ | f x y = (inc x) + y | ||
+ | where inc v = v + 1 | ||
+ | </code> | ||
+ | |||
+ | <code haskell> | ||
+ | f x y = (inc x) + (dec y) | ||
+ | where inc v = v + 1 | ||
+ | dec v = v - 1 | ||
+ | </code> | ||
+ | |||
+ | <code haskell> | ||
+ | f x y = (inc x) + (dec y) | ||
+ | where inc v = (g v) + 1 | ||
+ | where g 0 = 0 | ||
+ | g _ = 1 | ||
+ | dec v = v - 1 | ||
+ | </code> | ||
+ | |||
+ | <code haskell> | ||
+ | f x y = (inc x) + (dec y) | ||
+ | where inc v = (g v) + 1 | ||
+ | g 0 = 0 | ||
+ | g _ = 1 | ||
+ | dec v = v - 1 | ||
+ | </code> | ||
+ | |||
+ | 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 ====== | ===== Basic datatypes ====== | ||
Line 68: | Line 106: | ||
where ''a'' and ''b'' are type variables. | 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: | ||
+ | |||
+ | <code haskell> | ||
+ | f [] = ... | ||
+ | f (x:xs) = ... | ||
+ | </code> | ||
+ | |||
+ | 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: | ||
+ | <code haskell> | ||
+ | case <expression> of | ||
+ | <pattern 1> -> <body expression 1> | ||
+ | ... | ||
+ | <pattern n> -> <body expression n> | ||
+ | </code> | ||
+ | |||
+ | The ''<expression>'' 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: | ||
+ | <code haskell> | ||
+ | case foldr (:) [] [1,2,3] of | ||
+ | [] -> 1 | ||
+ | (x:xs) -> 2 | ||
+ | (x:y:xs) -> 3 | ||
+ | _ -> 4 | ||
+ | </code> | ||
+ | |||
+ | 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: | ||
+ | |||
+ | <code haskell> | ||
+ | <function name> <param1> ... <param n> | ||
+ | | <boolean condition 1> = <body expression 1> | ||
+ | ... | ||
+ | | <boolean condition n> = <body expression n> | ||
+ | | otherwise = <body expression (n+1)> | ||
+ | </code> | ||
+ | |||
+ | Example: | ||
+ | |||
+ | <code haskell> | ||
+ | f x y | ||
+ | | x + 5 > y = 1 | ||
+ | | x == y = 2 | ||
+ | | x == head (sort [x,y]) = 3 | ||
+ | </code> | ||
+ | |||
+ | In the previous example, ''f 1 5'' will return ''1'' while ''f 4 5'' will return ''3''. | ||
+ | |||