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:40] pdmatei |
pp:syntax [2020/03/12 23:18] (current) pdmatei |
||
---|---|---|---|
Line 60: | Line 60: | ||
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''. | 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 96: | Line 129: | ||
===== Pattern matching ====== | ===== Pattern matching ====== | ||
- | A powerful mechanism in Haskell is pattern matching, which allows expressing | + | 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''. | ||
- | |