Functions
Function application¶
Given a function, such as:
we can call it on input as follows:
One can also write add1(2), but Haskell convention is to avoid brackets where possible.
Arguments¶
In Haskell, a function has a single argument, but that argument can be a tuple of multiple values:
Currying¶
Another approach to taking multiple arguments, more commonly used, is shown here:
- By convention in Haskell,
Int -> Int -> IntmeansInt -> (Int -> Int), not(Int -> Int) -> Int.
As the type signature indicates, the first argument is an integer, but the output is a function which takes a second integer and returns an integer.
Accordingly, we can apply exampleFunc to an integer, say 5, and obtain an "add 5" function:
- By convention in Haskell,
exampleFunc 4 5means(exampleFunc 4) 5.
This is often referred to as partial application. exampleFunc would be described as a curried function.
Hint
When you see a function with a type like Type1 -> Type2 -> Type3 -> Type4 (which brackets as: Type1 -> (Type2 -> (Type3 -> Type4)) ), you can think of it as taking Type1, Type2 and Type3 as inputs, and outputting Type4.
Partial application for types¶
The same holds for types:
> :kind Either
Either :: * -> (* -> *)
> :kind Either Bool
Either Bool :: * -> *
> :kind Either Bool Int
Either Bool Int :: *
Pattern matching¶
When defining a function, you can pattern match:
exampleFunc :: Either Int Bool -> Int
exampleFunc (Left i) = i -- (1)!
exampleFunc (Right True) = 1
exampleFunc (Right _) = 0 -- (2)!
-
imatches anyInt. It has to be anIntbecause the input has typeEither Int Bool(Intis on the left). -
The underscore
_matchesBool. It has to be aBoolbecause the input has typeEither Int Bool(Boolis on the right).
Here is how the function behaves:
Here, (Left i), (Right True) and (Right _) are all patterns.
The patterns are matched top-down. For example, if the function were:
Then:
because line 3 would be matched before line 4 was reached.
Note
Pattern matching follows the definition of types, including custom types:
Note
Patterns can be arbitrarily deeply nested, as in:
data Color = Black | White
data Piece = Bishop Color | Knight Color
getColor :: Either Bool Piece -> Maybe Color
getColor (Right (Bishop c)) = Just c
getColor (Right (Knight c)) = Just c
getColor (Left _) = Nothing
And also with recursive types
Pattern matching lists¶
This also applies to lists:
Using @ in patterns¶
-- first example
> let whole@(a,b) = ('a', True)
> a
'a'
> b
True
> whole
('a',True)
-- second example
> let whole@(a, left@(b, c)) = ('a', (True, ()))
> a
'a'
> b
True
> c
()
> whole
('a',(True,()))
> left
(True,())
exampleFunc :: (Int, Bool) -> (Int, Bool)
exampleFunc whole@(int,bool)
| even int = whole
| otherwise = (int - 1, not bool)
Created: January 7, 2023