Functions
Recall the following from before:
doubleList xs = map ((*) 2) xs
squareList xs = map square xs
where square x = x ^ 2
The latter defines a helper definition to be used just once. There are several ways to avoid this: with lambdas, by flipping the order of function arguments, and with sections. No matter which version we choose, we can further simplify the definition via eta-reduction.
squareList xs = map (\x -> x ^ 2) xs -- lambda
squareList xs = map (flip (^) 2) xs -- flip
squareList xs = map (^2) xs -- section
squareList = map (^2) -- eta-reduction
Lambdas
All function definitions we have seen so far are syntactic sugar.
addOne x = 1 + x === addOne = \x -> 1 + x
add x y = x + y
=== add = \x -> \y -> x + y -- nested lambda
=== add = \x y -> x + y -- sugar for nested lambdas
Arrows associate to the right (in expressions and in types)
S -> (T -> U)
\x -> (\y -> e)
x :: S
y :: T
e :: U
So, "multi-argument" functions:
e1 e2 e3 e4 e5 === ((((e1 e2) e3) e4) e5)
e1 :: T2 -> (T3 -> (T4 -> T5))
e2 :: T2
e3 :: T3
e4 :: T4
e5 :: T5
Function Application and Composition
Flipping Arguments
Flip the order of first two arguments to a function.
flip :: (a -> b -> c) -> b -> a -> c
flip f y x = f x y
Currying / Uncurrying
curry :: ((a, b) -> c) -> a -> b -> c
curry f a b = f (a, b)
uncurry :: (a -> b -> c) -> (a, b) -> c
uncurry f (a, b) = f a b
Infix Application
Infix function application, along with its r
ight-associative
fixity declaration, allows nested function calls to be
right-associated without writing parentheses. Don't worry
too much about precedence levels (here 0
) for infix operators.
infixr 0 $
($) :: (a -> b) -> a -> b
($) f x = f x
e1 e2 e3 e4 e5 === ((((e1 e2) e3) e4) e5)
(e1 (e2 (e3 (e4 e5)))) === e1 $ e2 $ e3 $ e4 e5
Composition
compose :: (b -> c) -> (a -> b) -> a -> c
compose f g x = f (g x)
Infix operator composition.
(.) f g x = f (g x)
Alternate definition.
(f . g) x = f (g x)
Point-free notation.
squareAndDouble x = compose double square x
squareAndDouble = compose double square
squareAndDouble = double . square
Sections
A section is the partial application of a binary operation to one of its arguments.
(n op) === (op) n === (\m -> n op m)
(op m) === flip (op) m === (\n -> n op m)
> (+3) 4
> let plus3 = (+3) -- let plus3 x = x + 3
> (3+) 4
> let plus3 = (3+) -- let plus3 x = 3 + x
> (*1) == (1*)
> (1-) 10
> (-1) 10 -- special case: unary minus!
> (subtract 1) 10 -- poor man's "section"
> (^) 2 3
> (^) 2
> flip (^) 3
Eta-Reduction
f x = g x
f = g
f x = (BIG_COMPLICATED_EXPRESSION) x
f = (BIG_COMPLICATED_EXPRESSION)
Note that this eta-reduction pattern is only valid of
x
is not referred to in the BIG_COMPLICATED_EXPRESSION
.
For example:
double x = (2*) x
double = (2*)
square x = (^2) x
square = (^2)
squareAndDouble x = compose (*2) (^2) x
squareAndDouble = compose (*2) (^2)
squareAndDouble = (*2) . (^2)
squareAndDoubleList xs = map squareAndDouble xs
squareAndDoubleList xs = map ((*2) . (^2)) xs
squareAndDoubleList = map ((*2) . (^2))