I have been trying to understand this piece of code but I'm not able to wrap it up clearly:
ghci > :t zipWith
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
ghci > :t ($)
($) :: (a -> b) -> a -> b
ghci > let c = zipWith ($)
ghci > :t c
c :: [b -> c] -> [b] -> [c]
How does [b -> c] originate up in the above type signature ?
In order for zipWith ($) to typecheck we must unify the type of zipWith's first argument with the type of ($). I'll write them out together and with unique names to make it more clear.
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
($) :: ((x -> y) -> x -> y)
Thus, zipWith typechecks if and only if we can assume that a ~ (x -> y), b ~ x and c ~ y. There's nothing stopping this unification from succeeding, so we can substitute these names back into the type for zipWith.
zipWith :: ((x -> y) -> x -> y) -> [x -> y] -> [x] -> [y]
($) :: ((x -> y) -> x -> y)
And then proceed with application since everything matches up nicely now
zipWith ($) :: [x -> y] -> [x] -> [y]
which is equivalent up to the specific choice of type variable names with the type you saw.
It's just context substitution and no magic there. Look:
ghci > :t zipWith
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
ghci > :t ($)
($) :: (a' -> b') -> a' -> b'
Now consider zipWith ($). It has type of (a -> b -> c) -> [a] -> [b] -> [c] where first argument is fixed, so we should pattern match (a -> b -> c) (type of a first arg) with (a' -> b') -> a' -> b' (type of $). Thus we have a = (a' -> b'), b = a', c = b'. Substitute is back to the zipWith: [a' -> b'] -> [a'] -> [b'] (first argument is fixed, so he disappear from type) and that's exactly what you got with type variables named differently.
Also, one might consider zipWith semantics: take zipper (first argument), and then zip two lists together. If your zipper is function application ($ is function application, yes!) then when zipping two lists you just invoke elements of the first list with corresponding element of the second list. And function type reflects that.
The actual letters assigned in the type signature are arbitrary and could be anything. You could just as easily write the type of ($) as
(x -> y) -> x -> y
It takes two arguments, a function taking a single argument, and a value to pass into the function.
The first argument to zipWith is a function taking two arguments (a -> b -> c). Given the definition of ($), you choose a as (x -> y) and b as x then c is y, so you get the type of zipWith ($) as
[x -> y] -> [x] -> [y]
Let's rewrite a bit our signature:
($) :: (a -> b) -> a -> b
($) :: a' -> b' -> c'
where -- pseudo-Haskell
a' = a -> b
b' = a
c' = b
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
And find, that
zipWith ($) :: [a'] -> [b'] -> [c']
zipWith ($) :: [a -> b] -> [a] -> [b]
Related
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.
I am playing a bit with zipWith and encounter following:
Prelude Control.Applicative> :t zipWith id
zipWith id :: [b -> c] -> [b] -> [c]
Why does the compiler expect for the next argument a list of functions?
I tried to analyze, but could not conclude, why the next argument must be a list of functions.
How did the signature is getting apply, when I pass id to zipWith?
The type of zipWith is:
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
And the type of id is:
id :: d -> d
So if we now want to derive the type of zipWith id, we push the type of id :: d -> d into the type of the first argument of zipWith:
d -> d
~ a -> (b -> c)
So that means that: a ~ d and a ~ b -> c. So that means that the type of zipWith id is now:
zipWith id :: [a] -> [b] -> [c]
-> zipWith id :: [b -> c] -> [b] -> [c]
How does this work: the first list has to contain a list of functions f :: b -> c, and the second list, a list of elements x :: b, and it thus calculates a list of elements f x :: c.
For example:
Prelude> zipWith id [(+1),(5/),(3*),(3-)] [1,4,2,5]
[2.0,1.25,6.0,-2.0]
since 1+1 is 2.0, 5/4 is 1.25, 3*2 is 6.0 and 3-5 is -2.0.
So zipWith id will take two elements f and x, and apply id f x on these, or more verbose (id f) x. Since id f is f, it will thus calculate f x.
We can thus conclude that zipWith is an elementwise mapping.
Thank you, Willem Van Onsem for the great answer.
Let's understand zipWith id from the eyes of the type inference system of ghc.
first, consider the type of zipWith
Prelude> :info zipWith
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
-- Defined in ‘GHC.List’
First argument of zipWith is a function which accepts a function which takes two arguments.
(a -> b -> c) can also be re-written as a -> (b -> c)
now consider zipWith id. type of id is from a -> a
we have put id in a place where a two argument function must go.
So, type inference would make (a -> b -> c) look like a -> (b -> c) (notice a -> (b -> c) takes one arument a and gives b -> c i.e a single argument function.)
But, making a -> (b -> c) an identity function would be possible only if a is (b -> c).
When a is (b -> c) the function a -> b -> c becomes ((b -> c) -> (b -> c))
So, type inferencing system would infer a as (b -> c) and the resultant output would be [a] -> [b] -> [c] replacing a with b -> c.
Replace a with (b -> c).
Make (a -> b -> c) look like id. (a -> b -> c) can be made to look like id by the above replacement.
((b -> c) -> b -> c) which can also be written as ((b -> c) -> (b -> c)) which is id :: x -> x where x is (b -> c)
zipWith :: ((b -> c) -> b -> c) -> [b -> c] -> [b] -> [c]
So finally we get output as [b -> c] -> [b] -> [c]
Suppose I have a function like map zipWith, how can I determine its type? Given that the type of zipWith is (a -> b -> c) -> [a] -> [b] -> [c] and that of map is (a -> b) -> [a] -> [b]
Similarly how do I determine types of functions like zipWith sum?
You can check the type in GHCi with :t, as mentioned in the other answers. If you want to try figuring it out yourself, you need to substitute the types as appropriate. In your first example, we have
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
and
map :: (a -> b) -> [a] -> [b]
The first argument of map is a function of one argument, so we have to regard zipWith as such a function:
zipWith :: (a -> b -> c) -> ([a] -> [b] -> [c])
(The type above is equivalent to the original one. It means zipWith converts a function which takes arguments of types a and b to a function which takes lists of a and b.)
map can be seen as a function of one argument as well:
map :: (a -> b) -> ([a] -> [b])
Now, we fill in the types in map's result type - a becomes a -> b -> c and b becomes [a] -> [b] -> [c]:
map zipWith :: [a -> b -> c] -> [[a] -> [b] -> [c]]
P.S.: Do you really want functions which take lists of functions as arguments? If you just want to zip two lists adding the corresponding elements you want
zipWith (+) :: Num c => [c] -> [c] -> [c]
rather than zipWith sum.
You can see its type in ghci:
ghci> :t map zipWith
map zipWith :: [a -> b -> c] -> [[a] -> [b] -> [c]]
Similarly for zipWith sum:
ghci> :t zipWith sum
zipWith sum :: Num (b -> c) => [[b -> c]] -> [b] -> [c]
That's what ghci is there for! Or you can just use tryhaskell.org
> :t map zipWith
:: [a -> b -> c] -> [[a] -> [b] -> [c]]
So, let's straight to the point.
:t (map.foldr)
(map.foldr) :: (a1 -> a -> a) -> [a] -> [[a1] -> a]
What is [[a1] -> a]?
I'm really trying to understand this composition, so I was doing this:
-- map.foldr
map.foldr :: (a1 -> a -> a) -> [a] -> [[a1] -> a]
map :: (a1 -> b1) -> [a1] -> [b1]
(.) :: (y -> w) -> (x -> y) -> x -> w
foldr :: (a -> b -> b) -> b -> [a] -> b
y = (a1 -> b1) w = ([a1] -> [b1])
x = (a -> b -> b) y = (b -> [a] -> b)
y = (a1 -> b1)
y = (b -> [a] -> b)
_________________________
What happens in this point? Thank you!
To answer this question it's good to recall what foldr and map do.
The more complicated of the two is foldr, which has type
-- list to be folded
-- v
foldr :: (a -> b -> b) -> b -> [a] -> b
-- ^ ^
--folding function terminal value
The list to be folded is really a chain of conses (:) and a terminal empty list:
1 : 2 : 3 : []
The action of foldr is to replace the : and [] constructors with the folding function and the terminal value, respectively:
foldr (+) 0 (1 : 2 : 3 : []) == 1 + 2 + 3 + 0
The map function is simpler. One way of thinking of it is as taking a function and a list, and applying the function to every argument of the list:
map :: (a -> b) -> [a] -> [b]
-- ^ ^
-- function list
However, you can also think of it as taking a function, and lifting it to be a function that acts on lists instead:
map :: (a -> b) -> ( [a] -> [b] )
-- ^ ^
-- function function on lists
What does it mean to compose these two functions, map . foldr? Note that this is just applying the functions one after the other - in particular,
(map . foldr) f == map (foldr f)
Since you apply foldr first, you must be applying it to a function f :: a -> b -> b, and you get back another function:
foldr f :: b -> [a] -> b
-- ^ ^
--terminal val list to be folded
Now you apply map, which lifts the function to act on lists:
map (foldr f) :: [b] -> [[a] -> b]
-- ^ ^
--list of terminal vals functions that fold lists
This type looks odd, but it's valid. Now instead of a single terminal value, you give it a list of terminal values, and you get a list of folding functions back - one for each terminal value that you supplied.
To make it clearer we could look at a particular function, (+), which has type
(+) :: Num a => a -> a -> a
If we substitute that into the equation above, we get
(map . foldr) (+) :: Num a => [a] -> [[a] -> a]
-- ^ ^
-- list of terminal vals functions that fold lists
If we now apply it to the list [0, 1, 2] we get a list of three functions:
(map . foldr) (+) [0,1,2] :: Num a => [[a] -> a]
We can use the map ($x) idiom to apply each of the functions in the list to a particular argument. It has to be a list of numbers, and I'll choose [3,4,5]. Watch carefully:
> map ($[3,4,5]) ((map.foldr) (+) [0,1,2])
[12, 13, 14]
The list [3,4,5] was folded three times using (+) as the folding function, and each time with a different terminal value:
3 + 4 + 5 + 0 == 12
3 + 4 + 5 + 1 == 13
3 + 4 + 5 + 2 == 14
When the terminal value is 0, we simply get the sum of the values: 3 + 4 + 5 == 12. When the terminal value is 1 we get one more than the sum of the values (13) and when the terminal value is 2 we get two more than the sum of the values (14).
To continue where you left off, the two definitions of y must be equal:
y = (a1 -> b1) = (b -> [a] -> b)
= (b -> ([a] -> b))
so we can conclude that:
a1 = b
b1 = [a] -> b
The function composition has been supplied two function arguments, so the resulting type is just:
x -> w
But we know:
x = a -> b -> b
w = [a1] -> [b1] = [b] -> [[a] -> b]
So, the result type is:
(x -> w) = ((a -> b -> b) -> ([b] -> [[a] -> b]))
= (a -> b -> b) -> [b] -> [[a] -> b]
which is congruent to:
(a1 -> a -> a) -> [a] -> [[a1] -> a]
map.foldr :: (a1 -> a -> a) -> [a] -> [[a1] -> a]
map :: (a1 -> b1) -> [a1] -> [b1]
(.) :: (y -> w) -> (x -> y) -> x -> w
foldr :: (a -> b -> b) -> b -> [a] -> b
-- if you substitute: x = (a -> b -> b) y = (b -> [a] -> b)
-- then you get for map :: (b -> ([a] -> b)) -> [b] -> [[a] -> b]
-- so if composition operator applied:
map . foldr :: (a -> b -> b) -> [b] -> [[a] -> b]
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.