Higher order Functions
1 Parentheses!? atHome
Give examples for functions with the following types:
Float −> Float) −> Float
(Float −> (Float −> Float)
Float −> Float) −> (Float −> Float) (
-- For example (all these expressions have more general polymorphic types, but, in particular, type check at the requested types)
-> f 2.0 :: (Float −> Float) −> Float
\f (*) :: Float −> (Float −> Float)
.) (+1) :: (Float −> Float) −> (Float −> Float) (
Give an example program p
with the following type and explain how the function arrow ->
associates:
p :: Int -> Bool -> Int
-- For example
= if b then i else -i
p i b -- Indeed, the function arrow -> always associates to the right, so this type should be read as
-- the type Int -> (Bool -> Int) of curried multiple argument functions, rather than the type (Int -> Bool) -> Int of higher order functions
Give examples of programs foo
, bar
and baz
, such that foo bar baz
type checks and please explain how function application associates.
-- For example,
foo :: Int -> Int -> Int
= (+)
foo bar :: Int
= 1
bar baz :: Int
= 2
baz -- Then foo bar baz is well-typed at type Int
-- Indeed, function application always associates to the left so the expression should be read as (foo bar) baz
-- Another example would be
foo :: (a -> [a] -> [a]) -> [a] -> [a] -> [a]
= foldr
foo bar :: a -> [a] -> [a]
= as ++ [a]
bar a as baz :: [a]
= []
baz -- Then, foo bar baz (which should be read as (foo bar) baz) has type [a]
-- Note that function application associating to the left and function arrows associating to the right are nice behaviour because it means
-- that we can implement the default use-case of curried functions of multiple arguments and partial application of such functions without writing many parentheses in either the type or the program.
Can you give examples of programs foo
, bar
and baz
, such that one might expect foo bar baz
to be well-typed if function application were to associate in the opposite direction, but such
that foo bar baz
is in reality ill-typed?
-- For example,
foo :: Bool -> Bool
= not
foo bar :: Bool -> Bool
= not
bar baz :: Bool
= True
baz -- Then foo (bar baz) is well-typed at type Bool
-- However, as function application associates to the left in reality and not to the right, foo bar baz should be parsed as (not not) True, which does not type check.
2 Currying inClass
Please implement two functions
curry :: ((a, b) -> c) -> (a -> b -> c)
uncurry :: (a -> b -> c) -> ((a, b) -> c)
curry f a b = f (a, b)
uncurry g (a, b) = g a b
Can you convince yourself that curry
and uncurry
are mutually inverse? Try it out on some example functions!
Please simplify the types of curry
and uncurry
by removing unnecessary parentheses
curry :: ((a, b) -> c) -> a -> b -> c
uncurry :: (a -> b -> c) -> (a, b) -> c
3 Filter atHome
The function filter
can be defined in terms of concat
and map
:
filter p = concat . map box
where box x = _
Complete the definition of box
.
= if p x then [x] else [] box x
4 Function Composition atHome
Function composition first applies the latter of the supplied
functions to the argument, the former thereafter. Write a function
before
that can be used to rewrite f . g . h
to h `before` g
`before` f
. What can you say about associativity of (.)
and
before
?
before :: (a -> b) -> (b -> c) -> (a -> c)
= flip (.)
before -- both (.) and before are associative operations
5 Higher order data types atHome challenging
We have seen that [...]
is a type function that maps types to
types. Similarly because −>
is a type constructor mapping two types
to a type, for some c
also c −>
is a type function mapping a type
a
to c −> a
. Rewrite the type of map
by substituting the type
function [...]
by c −>
. Can you derive an implementation from the
resulting type?
mapFun :: (a -> b) -> (c -> a) -> (c -> b)
= (.) mapFun