So, we have three functions:
A :: [([String], [String])] -> [String] -> [String]
A = B . C "*" f
B :: (a -> Maybe a) -> a -> a
C :: Eq a => a -> ([a] -> [a]) -> [([a], [a])] -> [a] -> Maybe [a]
The thing that confuses me about this is that A takes two arguments, and those arguments will then be the arguments to C when A is called. Then the result of C will be the argument(s) to B, but how is that possible since B is supposed to take two arguments, when C like every other function I've seen so far in Haskell will just return one value.
From what I can gather from the dot operator:
(.) f g = \x -> f (g x)
Using the dot in A allows it to be this "point free" style, and makes it more readable. But what it says is:
A x y = B (C "*" f x y)
But I'm obviously missing something, since if I write it like that then:
* Couldn't match expected type `a0 -> Maybe a0'
with actual type `Maybe [[Char]]'
And then I'm stumped.
Using the dot in A allows it to be this "point free" style, and makes it more readable. But what it says is:
A x y = B (C "*" f x y)
What it says is:
A x = B (C "*" f x)
since B has type B :: (a -> Maybe a) -> a -> a, it will return a function, and thus we can add an extra parameter:
A x y = B (C "*" f x) y
but these are different. Note that the (.) makes a chain for one parameter.
After all (.) is implemented like you say as:
(.) :: (b -> c) -> (a -> b) -> a -> c
(.) f g x = f (g x)
So here B is the left function, and (C "*" f) is the right function. So that means that (.) B (C "*" f) is equivalent to:
(.) B (C "*" f) x = B (C "*" f x)
If we analyze the types, we see:
(.) :: (b -> c ) -> ((a -> b) -> (a -> c))
B :: (d -> Maybe d) -> (d -> d)
--------------------------------------------------------------
b ~ (d -> Maybe d), c ~ (d -> d)
so that means the type of (.) B is (a -> (d -> Maybe d)) -> (a -> (d -> d)), or less verbose (a -> d -> Maybe d) -> a -> d -> d.
Now we can analyze the type of C "*" f:
C :: Eq a => a -> ([a] -> [a]) -> [([a], [a])] -> [a] -> Maybe [a]
"*" :: String
f :: ([a] -> [a])
--------------------------------------------------------------------
a ~ String
We do not know what f is here, but we will assume that it is a [String] -> [String] function, so the matching works.
This thus means that C "*" f has type [([String], [String])] -> [String] -> Maybe [String].
Now the types match, since:
(.) B :: ( a -> d -> Maybe d ) -> a -> d -> d
C "*" f :: [([String], [String])] -> [String] -> Maybe [String]
---------------------------------------------------------------------------------
a ~ [([String], [String])], d ~ [String]
So this means that (.) B (C "*" f) has type [([String], [String])] -> String -> String. This indeed holds if we determine this with ghci:
Prelude> :{
Prelude| b :: (a -> Maybe a) -> a -> a
Prelude| b = undefined
Prelude| :}
Prelude> :{
Prelude| c :: Eq a => a -> ([a] -> [a]) -> [([a], [a])] -> [a] -> Maybe [a]
Prelude| c = undefined
Prelude| :}
Prelude> :t b . c "*" undefined
b . c "*" undefined
:: [([[Char]], [[Char]])] -> [[Char]] -> [[Char]]
Related
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]
I am studying a Haskell course since I am total beginner. There is a task which I am not able to solve although I tried many times.
The task is to determine the most general type of following function:
f x _ False = x
f _ x y = maybe 42 (g y) x
providing we know that g :: a -> [a] -> b
Can anyone help me? Thank you
I tried to determine y :: Bool, g :: Bool -> [Bool] -> b(?)
But I am not sure what should be "x" 'cause from the first row we could say that it can be maybe 42 (g y) x but there is another "x" in the expression.
So maybe the type of the f is f :: [Bool] -> [Bool] -> Bool -> [Bool]?
Let us start with the most generic type possible: the function takes three parameters, and thus returns an item, so we assume first that f has type:
f :: a -> b -> c -> d
We see that the third parameter matches with False so that is a Bool, so c ~ Bool. The first clause also assigns the first parameter to x and returns x, so that means that the type a of the first parameter, and that of the return type d is the same, so a ~ d.
We know that maybe has type maybe :: f -> (e -> f) -> Maybe e -> f, and 42 is the first parameter so has type f. An integer literal can have any Num type, so we know Num f. Since the return type of maybe 42 (g y) x is also f and we know that this is also a, we know that f ~ a, so we can "specialize" maybe in this case to maybe :: Num a => a -> (e -> a) -> Maybe e -> a. The third parameter in maybe 42 (g y) x is x, this is the second parameter in the f call (not to be confused with the first clause), so we know that b ~ Maybe e.
We also see a call g y with g :: g -> [g] -> h. The type of g y should match that of the second parameter of the maybe call, so that means that e ~ [g] and a ~ h. y has type Bool in the g y call, so that means that g ~ Bool, and thus the g function has type g :: Bool -> [Bool] -> a.
Now we thus have gathered the following types and equivalences:
f :: a -> b -> c -> d
maybe :: f -> (e -> f) -> Maybe e -> f
g :: g -> [g] -> h
a ~ d
c ~ Bool
Num f
f ~ a
b ~ Maybe e
g ~ Bool
e ~ [g]
a ~ h
g ~ Bool
This thus means that f has type:
f :: a -> b -> c -> d
-> f :: a -> b -> c -> a
-> f :: a -> b -> Bool -> a
-> f :: Num f => f -> b -> Bool -> f
-> f :: Num f => f -> Maybe e -> Bool -> f
-> f :: Num f => f -> Maybe [g] -> Bool -> f
-> f :: Num f => f -> Maybe [Bool] -> Bool -> f
Hence the most generic type is f :: Num f => f -> Maybe [Bool] -> Bool -> f, or we can rename the parameters to f :: Num a => a -> Maybe [Bool] -> Bool -> a.
We can let ghci do the work, for example with:
ghci> import Data.Maybe
ghci> :{
ghci| g :: a -> [a] -> b
ghci| g = undefined
ghci| :}
ghci> :{
ghci| f x _ False = x
ghci| f _ x y = maybe 42 (g y) x
ghci| :}
ghci> :t f
f :: Num p => p -> Maybe [Bool] -> Bool -> p
This thus means that ghci confirms this type.
The first thing to realize is that, for any equation, any variables defined in that equation, are scoped only to that equation. In particular, this means that the x on the first line is not related at all to the x on the second line. These are two separate variables, they just happen to have the same name.
Now, you have already correctly determined that y :: Bool and g :: Bool -> [Bool] -> b for some as of yet unknown b. And therefore, g y :: [Bool] -> b
The next thing to observe is that, since maybe :: p -> (q -> p) -> Maybe q -> p, which means that q ~ [Bool] (coming from the type of g y) and p ~ Int (coming from the literal 42). And therefore, g :: Bool -> [Bool] -> Int and x :: Maybe [Bool] (since it's used as the third parameter of maybe).
Finally, from the second equation, we can see that the function's return type is whatever maybe returns, which is Int. And therefore, that's also the type of the first parameter, because it's being returned in the first equation.
Taking all of that together:
f :: Int -> Maybe [Bool] -> Bool -> Int
One final stroke: I actually lied a little. The literal 42 is not really Int. Such literal can be any type that has an instance of the Num class. To account for that, let's replace our Ints in the signature with a generic type and give that type a Num constraint:
f :: Num a => a -> Maybe [Bool] -> Bool -> a
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.
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.