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 -> Int
meansInt -> (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 5
means(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)!
-
i
matches anyInt
. It has to be anInt
because the input has typeEither Int Bool
(Int
is on the left). -
The underscore
_
matchesBool
. It has to be aBool
because the input has typeEither Int Bool
(Bool
is 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