I know the title is a bit unclear, the problem is:
Suppose I have a function of type a -> c, another function of type b -> d, how can I get a function of type (a -> b) -> (c -> d), or is it impossible in general?
Probably I should provide some background. I asked this question because I have difficulty solving Exercise 9 from the paper Fun with phantom types.
data Type t where
...
RFun :: Type a -> Type b -> Type (a -> b)
And the tequalfunction
tequal :: Type t -> Type u -> Maybe (t -> u)
...
tequal (RFun a b) (RFun c d) = -- should do something with (tequal a c) (tequal b d)
So the problem boils down to composing a -> c and b -> d to get (a -> b) -> (c -> d)
This is impossible.
Suppose you have desired function f :: (a -> b) -> (c -> d).
You can simplify it's type to (a -> b) -> c -> d (See why).
How could implementation of f look like? It has first argument of type a -> b and second of type c:
f ab c = ...
What can you do with ab? It's a function but you can't apply it because you don't have anything of type a (except _|_). And even if you have functions g :: a -> c and h :: b -> d they are of no use because you don't have anything of type a or b and you can't compose them.
So the only valid implementation is something like
f ab = undefined
or
f = undefined
Regarding second part of your question, it seems that you can recursively use tequal to check function type equality: types a -> c and b -> d are equal only if a = b and c = d (this is valid because toy type system from the paper don't have type variables).
Here is a sketch of implementation:
tequal (RFun a c) (RFun b d)
= liftM2 func (tequal a b) (tequal c d)
You can note that the code is almost identical to the case for RPair. This is somehow related to currying.
As a small supplement to max taldykin's answer,
Given
f :: (a -> c) -> (b -> d) -> (a -> b) -> (c -> d)
f ac bd ab = ???
there's really only one way to combine the arguments
bd . ab :: a -> d
but now we're stuck! We have no way to construct a c -> d from any combination of a -> c, b -> d, a -> b, or a -> d.
On the other hand, if we had a c -> a then we could construct a
f :: (c -> a) -> (b -> d) -> (a -> b) -> (c -> d)
f ca bd ab = bd . ab . ca
By the way, it can be very helpful to get out a pen and paper and draw some arrows and try to connect them into a diagram:
If you try to do the same for f :: (a -> c) -> (b -> d) -> (a -> b) -> (c -> d) then you'll see that there's no way to draw a diagram that connects c -> d:
and now we have no way to connect the dots.
I believe that you are looking for Higher Order Functions, which is basically a function taking functions as parameters or returning other functions.
For example to illustrate a higher order function, you can define the following functions:
f0 :: Int -> Int
f0 x = 0
f1 :: a -> a
f1 x = x
f2 :: (a -> a) -> a -> a
f2 f a = f(a)
f3 :: (a -> a) -> (a -> a)
f3 f = f1
Notice that f2 takes a function and apply it to the second parameter and that f3 takes a function and return the function f1.
If you execute f2(f3(f0)) 5, it is going to return 5 to you.
Step-by-step
1- f2(f3(f0)) 5 f3 takes a function (f0) and will return f1.
2- f2(f1) 5 f2 takes a function (f1) and applies it to the second parameter (5)
3- f1(5) f1 is applied to 5.
Related
I struggle to understand how type declarations work...
Like these ones for example:
t :: (a -> b) -> (b -> c) -> a -> c
s :: (a -> b -> c) -> (a -> b) -> a -> c
I know from just trying different things that the correct functions would look like this:
t :: (a -> b) -> (b -> c) -> a -> c
t f g x = g (f x)
s :: (a -> b -> c) -> (a -> b) -> a -> c
s f g x = f x (g x)
But how does this work? Why are the brackets at the end? Why is it not
t f g x = (f x) g
or
s f g x = (f x) g x
Im so lost
For the first example:
t :: (a -> b) -> (b -> c) -> a -> c
In a type declaration, the type1 -> type2 pattern indicates a function from type1 to type2. In type declarations, the -> operator is right-associative, so this is parsed as:
t :: (a -> b) -> ((b -> c) -> (a -> c))
This kind of construction is called "currying": providing the first argument (type a -> b) yields a function which accepts the second argument (type b -> c) which yields a function which accepts the third argument (type a).
The function declaration syntax is set up to do this automatically. The first two arguments are functions and the third is just a, so start with names that reflect that: f :: a -> b and g :: b -> c are functions, while x :: a is a fully generic type which could be anything.
t f g x = ...
Note that function application in Haskell is just concatenation: to apply function f to value x, just use f x. This syntax is left-associative, so t f g x is parsed as (((t f) g) x) to match the currying construction described above.
Anyway, given these types, you don't have much choice in how to put them together:
the only thing you know about the type a is that it's the type of x, and the argument type of f, so the only thing you can do with them is to apply the function to the value: f x.
the only thing you know about the type b is that it's the result type of f and the argument type of g, so the only thing you can do is apply g (f x).
the only thing you know about the type c is that it's the result type of g and of the overall function t, so the only thing t can return is g (f x).
The reason you can't do (f x) g is that the types don't match:
f :: a -> b
x :: a
(f x) :: b
g :: b -> c
So, you can apply g :: b -> c to (f x) :: b to get a result of type c. But not the other way around, because b might not even be a function type.
This compiles successfully:
test = f id
f :: (a -> b -> c) -> String
f g = "test"
id has type a -> a, but f have a first argument of type (a -> b -> c).
I think it should not compile. I cannot understand it.
Because you can bind the uninstantiated variable. f :: (a -> b -> c) -> String says that for any 3 types a, b and c, f takes a function from a to b to c and returns string.
Its important to remeber that f :: (a -> b -> c) -> String is equivalent to f :: (a -> (b -> c)) -> String because of currying.
id takes any type, and returns that type.
So if we swap that in, id is returning b -> c, and taking a, so if a can be b -> c which it can as a can be any type, then this is fine.
Short version: it works because id can take and return functions -- and so can behave as if it were a two-argument function. Now for the long version.
id is a function that takes a value and returns it unchanged. Because it's so simple, it can do that for all kinds of different values -- Ints and Bools and Strings and lists and trees...
id :: Int -> Int
id :: Bool -> Bool
id :: String -> String
id :: [(b, c)] -> [(b, c)]
id :: Tree b -> Tree b
id :: (b -> c) -> (b -> c)
...and functions, why not!
f is a function that takes two-argument functions and ignores them. Because its handling of the function you give it is so simple, it can do that for all kinds of different two-argument functions -- addition, concatenation, pairing, ignoring, printing...
f :: (Int -> Int -> Int) -> String
f :: ([b] -> [b] -> [b]) -> String
f :: (b -> c -> (b, c)) -> String
f :: (b -> c -> b) -> String
f :: (Handle -> String -> IO ()) -> String
f :: ((b -> c) -> b -> c) -> String
...and the operation that takes a function and an argument and applies the one to the other, why not!
Ah, but two argument functions are really just one-argument functions that themselves return a function. So these two types are really just different spellings of the same type:
(b -> c) -> (b -> c)
(b -> c) -> b -> c
And, it turns out, the two behavior descriptions I gave for these two types also turn out to be the same. Recall they were:
A function that takes a function as an argument and returns it unchanged.
A function that takes a function as an argument, and an argument for that function, and applies one to the other.
I'll let you stew for a bit on why those turn out to be the same!
In any case, at this point it should be much more clear why this works. And, for the mechanical version of the answer, we can take these two types:
id :: a -> a
f :: (a -> b -> c) -> String
and make them fit together by choosing a ~ b -> c for both. (It is just a coincidence that the two instantiations of a are to the same type -- this is not generally the case when doing unification!) After instantiating a to b -> c in both type signatures, we get:
id :: (b -> c) -> b -> c
f :: ((b -> c) -> b -> c) -> String
I've begun reading the book Thinking With Types which is my first foray into type level programming. The author provides an exercise and the solution, and I cannot understand how the solution provided is correct.
The exercise is
I attempted to solve this exercise with the following
productToRuleMine :: (b -> a, c -> a) -> Either b c -> a
productToRuleMine (f, _) (Left b) = f b
productToRuleMine (_, g) (Right c) = g c
productFromRuleMine :: (Either b c -> a) -> (b -> a, c -> a)
productFromRuleMine f = (f . Left, f . Right)
productToRuleMine . productFromRuleMine = id
I feel this is a valid solution, however the book gives a different solution that doesn't seem to type check, leading me to believe my overall understanding is flawed
productToRuleBooks :: (b -> a) -> (c -> a) -> (Either b c) -> a
productToRuleBooks f _ (Left b) = f b
productToRuleBooks _ g (Right c) = g c
productFromRuleBooks :: (Either b c -> a) -> (b -> a, c -> a)
productFromRuleBooks f = (f . Left, f . Right)
The only way I can get the books answer to type check is the following:
(uncurry productToRule1 . productFromRule1) = id
since the type signatures alone don't line up
(Either b c -> a) -> (b -> a , c -> a)
(b -> a) -> (c -> a) -> (Either b c) -> a
So the question I have is, is my solution incorrect? Why does the books type signature for productToRuleBooks accept as it's first and second arguments the functions b -> a and c -> a when it's my understanding that x (multiplication) from algebra equates to the (,) in types, so why doesn't the books answer have (b -> a, c -> a) as the first argument?
lets say I have:
t = \x y -> x.y
its type is then:
*Main> :t t
t :: (b -> c) -> (a -> b) -> a -> c
If I understood correctly, x.y could be written as x(y), which means we first solve y which is (b -> c) and then x which is (b -> a) and a -> c is just are parameters in function; We give a (x) and c (y). Is this correct? If not how do I read this? what does (b -> c) -> (a-> b) mean and which one is x which one y.
And how do I read this:
t1 = \x y z -> x.y.z
*Main> :t t1
t1 :: (b1 -> c) -> (b2 -> b1) -> (a -> b2) -> a -> c
What is here b1 what b2 and how does this exactly work? Why is at the and just a -> c when I have 3 parameters as input? Please help me understand this
A better way to write the function type (b -> c) -> (a -> b) -> a -> c would be (b -> c) -> (a -> b) -> (a -> c) where there are explicit parentheses around (a -> c). This rewriting attempts to make it clear that the function does not take three arguments and return something of type c. It takes two arguments and returns a function of type (a -> c). This is the concept of currying, and it is integral to Haskell programming1.
In your first example, (b -> c) is x and (a -> b) is y. The return type (a -> c) is the new function obtained by composing the parameter functions.
Your second example can be interpreted in the same way. (b1 -> c) corresponds to x, (b2 -> b1) to y and, (a -> b2) to z. The return type is obtained by first composing z and y to get a function of type (a -> b1) then composing this function with a to get a function of type (a -> c). This function is what is ultimately returned.
1: In fact it is most correct to say that the function takes one argument of type (b -> c) and returns a function of type (a -> b) -> (a -> c) but, that's not particularly important.
I am a little confused about what the "partial" application of flip might do.
Since the type of the flip function is:
flip :: (a -> b -> c) -> b -> a -> c
which we can write without the parenthesis as:
flip :: a -> b -> c -> b -> a -> c
How can I partially apply it to only the first argument a? To get a function with the type:
flipa :: b -> c -> b -> a -> c
Or it doesn't make sense?
For example if I have something like:
let foo a b = (Just a, b)
:t foo
> foo:: a -> t -> (Maybe a, t)
It makes sense to partially apply it:
let a = foo 1
:t a
a :: t -> (Maybe Integer, t)
It doesn't make sense. The signature
f :: a -> b -> c
is equivalent to
f :: a -> (b -> c)
and not equivalent to
f :: (a -> b) -> c
This convention is why you can partially apply function in Haskell in the first place. Since all functions are curried by default, the signature f :: a -> b -> c can be interpreted as
f takes a and b, and returns c
or can equally validly be interpreted as
f takes a, and returns a function that takes b and returns c
(a -> b -> c) -> b -> a -> c is not the same as a -> b -> c -> b -> a -> c because the -> operator is right-associative, not left-associative. Therefore, partially applying flip is meaningless because it only has one parameter in the first place.
Also, your example doesn't make much sense because it would still produce an output function taking an a, which you would presumably not want. But if you take that out, you get a function which takes a unary function and produces exactly the same unary function, so just partially apply the original function and you're done.
As others have noted, the type (a -> b -> c) -> b -> a -> c is not the same as a -> b -> c -> b -> a -> c.
However, it is the same as (a -> b -> c) -> (b -> a -> c).
That shows that flip is a function that takes a single argument as input and therefore can't be partially applied*.
*: from the point of view of that flip returns a function of type b -> a -> c, which is not the only valid point of view in Haskell.