Haskell - Determining the annotation type of `map` and a `function` - haskell

I was going over a self-graded quizes for Haskell MOOC and could not figure out the following question:
If f :: a -> b, then what is the type of map (.f)?
where the answer is the following:
[b -> c] -> [a -> c] -- correct answer
-- below are wrong answers
[c -> a] -> [c -> b]
(b -> c) -> [a -> c]
[a] -> [b]
I understand that map's type is (a -> b) -> [a] -> [b] according to the map's documentation so I can sort of see why you would get a list ([a -> c]) for the output.
However, I don't understand why the input for this becomes a list ([b -> c]) given that the function f is a -> b. Since the input is a function, why wasn't the input type (b -> c)? (also, why is it using b -> c instead of a -> b?)
Furthermore, I was wondering why it's .f rather than f; I thought that the function composition (.) needed to be in the following format: map . f, but does .f mean something else in this case?
Thank you in advance for your assistance.

Deducing the type of some expression is a mechanical work, and kind of fun!
-- You can use :t exp in ghci to cheat, but is worth doing it by hand a few times
-- suggestion: For each expression use different set of variables
-- 1) write every part and its type
(.) :: (b -> c) -> (a -> b) -> a -> c
f :: x -> z
map :: (m -> n) -> [m] -> [n]
-- 2) build small steps.
-- In ( . f) you are applying f as the second argument of .
-- you only need to be carefull with variable names.
-- Align arguments as they are applied
(.) :: (b -> c) -> (a -> b) -> a -> c
f :: x -> z
-- Hence (symbol a ~ b means a is the same type as b)
a ~ x
b ~ z
-- Consuming the second output and use the names of f to make it clearer
(.) :: (b -> c) -> (a -> b) -> a -> c
f :: x -> z
( . f) :: (z -> c) -> x -> c
-- 3) do the same with the whole expresion
-- map ( . f) uses (. f) as its first argument
-- Tricky. Notice that the WHOLE expression (. f) is just the first argument of map
map :: ( m -> n ) -> [m] -> [n]
( . f) :: ( (z -> c) -> x -> c )
-- Hence. Notice that m and n are
m ~ z -> c
n ~ x -> c
-- Hence, comsume the first parameter and substite variable names
map :: ( m -> n ) -> [m] -> [n]
( . f) :: ( (z -> c) -> x -> c )
map ( . f) :: [z -> c] -> [x -> c]
-- 4) make var names more digestive. Keep in mind that in step 2) x ~ a and b ~ z
map ( . f) :: [b -> c] -> [a -> c]

Related

Calculating the type of `map . foldr`

map :: (a -> b) -> [a] -> [b]
foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
What is a systematic way to figure out the type for map . foldr ? I know how to do it for map foldr but get confused when it comes to a composition.
Thanks!
Obviously there must be a systematic way, otherwise the Haskell compiler could not do type inference.
One way we can do this ourselves is insert the types step by step:
We have the following types:
(.) :: (b -> c) -> (a -> b) -> (a -> c)
map :: (a' -> b') -> [a'] -> [b']
foldr :: Foldable t => (a'' -> b'' -> b'') -> b'' -> t a'' -> b''
Note that you have to choose different names for types appearing in different signatures for this to work out.
1. supply map to (.)
If we supply a generic function f to (.) we get the following types:
(.) :: (b -> c) -> (a -> b) -> (a -> c)
(.) f :: (a -> b) -> (a -> c)
f :: (b -> c)
choose f to be map:
map :: (a' -> b') -> [a'] -> [b']
equal to
map :: (a' -> b') -> ([a'] -> [b'])
as f has type (b -> c) we can conclude:
b :: (a' -> b')
c :: ([a'] -> [b'])
insert our inferred types:
(.) f :: (a -> b) -> (a -> c)
(.) map :: (a -> (a' -> b')) -> (a -> ([a'] -> [b']))
we can drop some parentheses:
(.) map :: (a -> (a' -> b')) -> a -> ([a'] -> [b'])
(.) map :: (a -> (a' -> b')) -> a -> [a'] -> [b']
(.) map :: (a -> a' -> b') -> a -> [a'] -> [b']
2. supply foldr to (.) map
Again start by suppling a generic function g:
(.) map :: (a -> a' -> b') -> a -> [a'] -> [b']
(.) map g :: a -> [a'] -> [b']
g :: (a -> a' -> b')
choose g to be foldr:
foldr :: Foldable t => (a'' -> b'' -> b'') -> b'' -> t a'' -> b''
equal to
foldr :: Foldable t => (a'' -> b'' -> b'') -> b'' -> (t a'' -> b'')
as g has type (a -> a' -> b') we can conclude:
a :: (a'' -> b'' -> b'')
a' :: b''
b' :: Foldable t => t a'' -> b''
insert our inferred types:
(.) map foldr :: a -> [a'] -> [b']
(.) map foldr :: Foldable t => (a'' -> b'' -> b'') -> [b''] -> [t a'' -> b'']
Which is the same type we get, when asking ghci for the type:
> :t ((.) map foldr)
((.) map foldr) :: Foldable t => (a1 -> a2 -> a2) -> [a2] -> [t a1 -> a2]
map . foldr is actually (.) map foldr. Adding the type of (.) into the mix we get
foldr :: Foldable t => (a -> (r->r)) -> (r -> (t a -> r))
map :: (i -> j) -> ([i] -> [j])
(.) :: ( b -> c ) -> ( d -> b ) -> (d -> c)
-----------------------------------------------------------------------------------------
-- 4 2 1 3
-----------------------------------------------------------------------------------------
(.) map foldr :: Foldable t => (d -> c)
where d ~ a -> (r -> r) -- 1
c ~ [i] -> [j] -- 2
b ~ r -> (t a -> r) -- 3
~ i -> j -- 4
-------------------
i ~ r -- 5
j ~ t a -> r -- 6
thus
map . foldr :: Foldable t => a -> (r -> r) -> [i] -> [j] -- by 1,2
~ Foldable t => a -> (r -> r) -> [r] -> [t a -> r] -- by 5,6
Here we used the application type derivation rule,
f :: A -> B
x :: A
---------------
f x :: B
(otherwise known as modus ponens, in logic).
We could also use a composition type derivation rule which is the application rule specialized for (.), or equivalently (>>>) = flip (.):
g :: B -> C
f :: A -> B
------------------------
f >>> g :: A -> C
g . f :: A -> C
To fit this pattern, we write the types down a bit differently, and obtain the result immediately:
map :: (i -> j ) -> ([i] -> [ j ])
foldr :: Foldable t => (a -> (r->r)) -> (r -> (t a -> r))
------------------------------------------------------------------------------------
foldr >>> map :: Foldable t => (a -> (r->r)) -> [r] -> [t a -> r]
map . foldr :: Foldable t => (a -> (r->r)) -> [r] -> [t a -> r]
It is much more intuitive this way.
Ok, rather than using an automatic method to infer the type I thought maybe you’ll be interested in a more intuitive answer:
As I’m sure you know, map . foldr is equivalent to (\x -> map (foldr x)). Let’s start with that.
What should be the type of x ? Well, since it’s the first parameter to foldr, it should look like a function that takes some value, some accumulator, and return something of the same type as the accumulator (by definition of foldr). Thus :
x :: (a -> b -> b)
Now that we have the type of the first parameter, let’s look at the rest.
Once (foldr x) is applied, we get back a function that stills waits for an initial accumulator value, and then for any foldable type, and returns a value of the same type as the accumulator (for example, the sum of every element in a list).
So the type of (foldr x) should be
Foldable t => b -> t a -> b
Ok but we're not done, let’s see what happens with the use of map now.
map should first be given a function (by definition). The return value of (foldr x) is seen as that, which means that this use of map considers that (b -> t a -> b) is the type of the function that needs to be applied to every element of a list.
Maybe it’s clearer written as (b -> (t a -> b)). So, this use of map considers that it is given a function that takes some input of type b and returns a function that itself takes a foldable a and returns a b.
Ok we’re almost there. Now, map still needs another argument: a list which elements are of the same type as the input of the function it will apply. So since the function we want to apply (the result of (foldr x)) takes a b, our use of map will take a [b].
So now we have :
(a -> b -> b) -> [b] -> …
We’re just lacking the type of the output value of that function composition, which is the type of the output value of this specific use of map. Since the function that is applied with map returns something of type (t a -> b), then the list of thing we will obviously return will be of type [t a -> b].
So in the end you have
Foldable t => (a -> b -> b) -> [b] -> [t a -> b]
as the type of map . foldr.

Why does (.) map have this type?

I tried to find the type of the function (.) map but somehow find that it is ((a -> d) -> (a -> e)) -> ([d] -> [e]) which according to GHCI is not correct because it should be (.) map :: (a1 -> a2 -> b) -> a1 -> [a2] -> [b].
What am I doing wrong?
Deriving the type...
We have as ingredients:
(.) :: (b -> c) -> (a -> b) -> a -> c
map :: (d -> e) -> [d] -> [e]
(here I used different type identifiers for the two functions to avoid any confusion). A more verbose form (where we make it more explicit that every function takes exactly one parameter) is:
(.) :: (b -> c) -> ((a -> b) -> (a -> c))
map :: (d -> e) -> ([d] -> [e])
Since map is the first parameter of (.) that means that its type (d -> e) -> ([d] -> [e]) should match the input type of the (.) function (so b -> c). This thus means:
b -> c
~ (d -> e) -> ([d] -> [e])
------------------------------
b ~ (d -> e), c ~ ([d] -> [e])
So that means that the result type of (.) map is:
(a -> b) -> (a -> c)
which is equivalent to:
(a -> (d -> e)) -> (a -> ([d] -> [e]))
or less verbose:
(.) map :: (a -> d -> e) -> a -> [d] -> [e]
... and its implementation
The (.) function can be seen as (.) f g == \x -> f (g x). So that means that our function
h = (.) map
is equivalent to:
h f x = map (f x)
It thus takes as input a function f and an object x, and than performs a map with f x as function.
Semancially you could say that we make a "map where one has to inject a 'contect'-objecct" of type a. This context is then taken into account by the processor. This could be useful if we want to apply multiple maps, each with a small change, and thus first pass a "context-object". This is of course an interpretation of humans. For a compiler, the x can have any use, interpretation, etc.
You have probably tried to match the functions by looking at the definition
Types of the two functions
(.) :: ((b -> c) -> (a -> b) -> a -> c)
map :: (d -> e) -> [d] -> [e]
and then trying to match d to b and e to c. Which gives you ((a -> d) -> (a -> e)) -> ([d] -> [e]), now you could match [d] to a and [e] to d. This is however not correct because according to the type definition of map, e and d could be of different type, i.e. d could be of type [e] but it doesn't have to.
The correct way to find the type of this function is to look at the definition of the types
Types of the two functions
(.) :: ((b -> c) -> (a -> b) -> a -> c)
map :: (d -> e) -> [d] -> [e]
and then to match (d -> e) to b and [d] -> [e] to c which gives you (a -> (d -> e)) -> a -> ([d] -> [e]), by removing the superfluous brackets and renaming the type variables you get (a -> b -> c) -> a -> [b] -> [c]. This is the same result GHCI gives you.
When I don't understand the type of a function, I write types using different letters:
(.) :: (b -> c) -> (a -> b) -> a -> c
map :: (x -> y) -> [x] -> [y]
now we are providing map as the first argument of (.) so we can deduce:
b -> c == (x -> y) -> [x] -> [y] -- by matching first arguments we get...
b == x -> y
c == [x] -> [y]
since we have already provided the first argument of (.), the whole b -> c part disappears.
(.) map :: (a -> b) -> a -> c -- Using the above equations for b and c
(.) map :: (a -> x -> y) -> a -> [x] -> [y] -- changing variables names
(.) map :: (a1 -> a2 -> b) -> a1 -> [a2] -> [b]
as GHCi plots

What is the justification for the type of unfoldr in Haskell?

The example given in the documentation of unfoldr :: (b -> Maybe (a, b)) -> b -> [a]:
unfoldr (\b -> if b == 0 then Nothing else Just (b, b-1)) 10
can easily be written with a redundant pair:
unfoldr (\b -> if b == 1 then Nothing else Just (b-1, b-1)) 11
What does unfoldr need the pair (a,b) for? Why is its type not (a -> Maybe a) -> a -> [a]?
A function with type
(a -> Maybe a) -> a -> [a]
restricts the output list element type to be the same as the state which is threaded through the generation process. unfoldr is more general in that it allows an independent type of state to be used.
Leveraging a bit of theory, one can recover the types for folds/unfolds of a recursive type, including lists, understanding why they are what they are.
Let A be a fixed type. The type "list of As" satisfies the isomorphism
List ~~ Either () (A, List)
which can be read "a list value is either a special value (the empty list), or a value of type A followed by a list value".
In a more succinct notation we could write Either as an infix +:
List ~~ () + (A, List)
Now, if we let F b = () + (A, b), we have that
List ~~ F List
The above is a fixed point equation, which always arises when using recursive types. For any recursive type defined by T ~~ F T we can
derive the type of the related folds/unfolds (also known as cata/ana or induction/coinduction principles)
fold :: (F b -> b) -> T -> b
unfold :: (b -> F b) -> b -> T
In the case of lists, we then obtain
fold :: ((() + (A, b)) -> b) -> List -> b
unfoldr :: (b -> (() + (A, b))) -> b -> List
The unfold can also be rewritten noting that Maybe c ~~ () + c:
unfoldr :: (b -> Maybe (A, b)) -> b -> List
The fold can instead be rewritten using
((x + y) -> z) ~~ (x -> z, y -> z)
getting
foldr :: (() -> b, (A, b) -> b) -> List -> b
then, since () -> b ~~ b,
foldr :: (b, (A, b) -> b) -> List -> b
finally, since (x, y) -> z ~~ x -> y -> z (currying), we have
foldr :: b -> ((A, b) -> b) -> List -> b
and again:
foldr :: b -> (A -> b -> b) -> List -> b
and with a final flip x -> y -> z ~~ y -> x -> z:
foldr :: (A -> b -> b) -> b -> List -> b
How do those types follow from the (co)induction principles?
Domain theory states that least fixed points (F(T)=T) coincide with least
prefixed points (F(T)<=T), when F is monotonic over a certain poset.
The induction principle simple states that, if T is the least prefixed point, and F(U)<=U, then T<=U. (Proof. It is the least!. QED.)
In formulae,
(F(U) <= U) ==> (T <= U)
To deal with fixed points over types, we need to switch from posets to categories, which makes everything more complex. Very, very roughly, every <= is replaced with some morphism. For instance, F(U)<=U now means that there is some morphism F U -> U. "Monotonic F" means that F is a functor (since a<=b implying F(a)<=F(b) now becomes (a->b) implying F a -> F b). Prefixed points are F-algebras (F u -> u). "Least" becomes "initial". And so on.
Hence the type of fold: (implication is -> as well)
fold :: (F u -> u) -> (T -> u)
Unfold derives from the coinduction principle, which deals with greatest postfix points T (which become coalgebras)
(U <= F(U)) ==> (U <= T)

Function gets four arguments instead of three - why doesn't this break?

Reading "Real World Haskell", on page 95 the author provides an example:
myFoldl f z xs = foldr step id xs z
where step x g a = g (f a x)
My question is: Why does this code compile? foldr takes only three arguments - but here, it is passed four: step, id, xs, z.
For example, this doesn't work (because sum expects one):
sum filter odd [1,2,3]
instead I must write:
sum $ filter odd [1,2,3]
Here's the type of foldr:
Prelude> :t foldr
foldr :: (a -> b -> b) -> b -> [a] -> b
Can we figure out how it becomes a four-argument function? Let's give it a try!
we're giving it id :: d -> d as the second parameter (b), so let's substitute that into the type:
(a -> (d -> d) -> (d -> d)) -> (d -> d) -> [a] -> (d -> d)
in Haskell, a -> a -> a is the same as a -> (a -> a), which gives us (removing the last set of parentheses):
(a -> (d -> d) -> (d -> d)) -> (d -> d) -> [a] -> d -> d
let's simplify, by substituting e for (a -> (d -> d) -> (d -> d)) and f for (d -> d), to make it easier to read:
e -> f -> [a] -> d -> d
So we can plainly see that we've constructed a four-argument function! My head hurts.
Here's a simpler example of creating an n + 1-argument function from an n-arg func:
Prelude> :t id
id :: a -> a
id is a function of one argument.
Prelude> id id id id id 5
5
But I just gave it 5 args!
It's because of how polymorphic foldr is:
foldr :: (a -> b -> b) -> b -> [a] -> b
Here, we've instantiated b to a function type, let's call it c -> c, so the type of foldr specializes to (for example)
foldr :: (a -> (c -> c) -> (c -> c)) -> (c -> c) -> [a] -> c -> c
foldr only takes 3 arguments
Wrong. All functions in Haskell take exactly 1 argument, and produce exactly 1 result.
foldr :: (a -> b -> b) -> b -> [a] -> b
See, foldr takes one argument (a -> b -> b), and produces 1 result: b -> [a] -> b. When you see this:
foldr step id xs z
Remember, it is just shorthand for this:
((((foldr step) id) xs) z)
This explains why this is nonsense:
sum filter odd [1,2,3]
(((sum filter) odd) [1,2,3])
sum :: Num a => [a] -> a takes a list as its input, but you gave it a function.

Explicit type signatures for polymorphic types. Part II

This is a follow up to a previous question; I got an answer I didn't really understand, but accepted. So I'll ask it again.
I still don't understand how this makes sense:
type Parse a b = [a] -> [(b,[a])]
build :: Parse a b -> ( b -> c ) -> Parse a c
build p f inp = [ (f x, rem) | (x, rem) <- p inp ]
Now, obviously, p binds to the first argument of type Parse a b. And, again obviously f binds to the second argument (b -> c). My question remains what does inp bind to?
If Parse a b is a type synonym for [a] -> [(b,[a])] I thought from the last question I could just substitute it:
build :: [a] -> [(b,[a])] -> ( b -> c ) -> [a] -> [(c,[a])]
However, I don't see that making any sense either with the definition:
build p f inp = [ (f x, rem) | (x, rem) <- p inp ]
Would someone explain type synonyms?
Now, obviously, p binds to the first argument of type Parse a b. And, again obviously f binds to the second argument (b -> c). My question remains what does inp bind to?
The argument of type [a]
If Parse a b is a type synonym for [a] -> [(b,[a])] I thought from the last question I could just substitute it:
build :: [a] -> [(b,[a])] -> ( b -> c ) -> [a] -> [(c,[a])]
Almost; you need to parenthesize the substitutions:
build :: ([a] -> [(b,[a])]) -> ( b -> c ) -> ([a] -> [(c,[a])])
Because -> is right-associative you can remove the parentheses at the end, but not at the beginning, so you get:
build :: ([a] -> [(b,[a])]) -> ( b -> c ) -> [a] -> [(c,[a])]
This should make it obvious why inp has type [a].
You can substitute -- but don't forget to bracket! That should be:
build :: ( [a] -> [(b,[a])] ) -> ( b -> c ) -> ( [a] -> [(c,[a])] )
Because the function arrow is right-associative you can dump the right-hand set of brackets, but crucially you cannot discard the new ones on the left:
build :: ( [a] -> [(b,[a])] ) -> ( b -> c ) -> [a] -> [(c,[a])]
So now when you have the line build p f inp, you can see that:
p :: ( [a] -> [(b,[a])] )
f :: ( b -> c )
inp :: [a]
So then we can see that:
p inp :: [(b, [a])]
And thus:
x :: b
rem :: [a]
And:
f x :: c
(f x, rem) :: (c, [a])
And hence the whole list comprehension has type [(c, [a])] -- which neatly matches what build should return. Hope that helps!
If you substitute
type Parse a b = [a] -> [(b,[a])]
into
build :: Parse a b -> ( b -> c ) -> Parse a c
you get
build :: ([a] -> [(b,[a])]) -> (b -> c) -> [a] -> [(c,[a])]
Remember that x -> y -> z is shorthand for x -> (y -> z) which is very different from (x -> y) -> z. The first is a function that takes two arguments x, y and returns z [precisely it takes one argument x and returns a function, that takes y and returns z]; the second is something that takes a function x -> y and returns z.
The important thing to remember here is that the arrow -> in type signatures is right associative. The type a -> (b -> c) is the same as the type a -> b -> c.
So the type
Parse a b -> ( b -> c ) -> Parse a c
resolves to
([a] -> [(b,[a])]) -> ( b -> c ) -> ([a] -> [(c,[a])])
By associativity, you can remove the last parens, but not the first. That gives you
([a] -> [(b,[a])]) -> ( b -> c ) -> [a] -> [(c,[a])]
which allows you to write a formula for build with 3 arguments.

Resources