I struggle understanding function composition type results e.g
ghci> :t (id . const)
(id . const) :: a -> b -> a
ghci> :t ((:) . (+))
((:) . (+)) :: a -> [a -> a] -> [a -> a]
How do you guys generally derive function composition types?
That's simple, initially write down the types of both the functions:
> :t const
const :: a -> b -> a
> :t id
id :: a -> a
(id . const) gets translated to \x -> id (const x)
(const x) :: b -> a -- (Note that the type of `x` is `a` here)
id (const x) :: b -> a -- The output of id type will be the same as the input it takes
\x -> id (const x) :: a -> b -> a -- We aleady know the type of x
You can follow the same step for the next function.
Lets see that
> :t (.)
(.) :: (b -> c) -> (a -> b) -> a -> c
So in general:
Given two functions. Consider them as curried functions of one parameter, i.e. function of type
a -> b -> c -> ... -> z we'll consider as a function a -> ( b -> c -> ... -> z ) of one parameter that returns function with one parameter less.
Result type of second function should be same as parameter type of first. So consider parameter of first function as result parameter of second. Denote it as b.
Result type of whole composition should be equal a function from parameter type of second function (denote it as a) to result type of first function (denote it as c)
> :t (id . const)
> :t id
id :: a -> a
rename it to x -> x
> :t const
const :: a -> b -> a
Note that a here is not necessary the same that in previous type equation, some rename it to y -> z -> y
a = y
b = z -> y = x
c = x = z -> y
So result is a -> c = y -> ( z -> y) same as a -> b -> c
Overall semantics is equal to const
> :t ((:) . (+))
> :t (:)
(:) :: a -> [a] -> [a]
rename it to x -> [x] -> [x]
> :t (+)
(+) :: Num a => a -> a -> a
rename it to Num y => y -> y -> y
a = y
b = y -> y = x
c = [x] -> [x] = [y -> y] -> [y -> y]
Also we have restriction with type class Num y
So overall type well be Num y => a -> c = y -> [y -> y] -> [y -> y] same as Num a => a -> [a -> a] -> [a -> a]
Overall semantics is "make single parameter function that adds first parameter to numeric value and prepend that function to a given list of single parameter functions"
Exercises: Find the types of the function compositions below, by hand, not using GHCi. Do these exercises to build intuition for reasoning about function types. Remember that type a -> a, where a is a type variable, matches not only Int -> Int or [Bool] -> [Bool], but (a -> a) -> (a -> a) too. If you can't do the exercises, read below.
(*3) . (+2) (easy)
(*) . (+2)
(:) . (+2)
(:) . (*)
What is a function? It's something, that takes an input and returns an output. Not just random output, but output that depends on that input. For the same input the same function always returns the same output. A function transforms some stuff to another stuff. Input and output can be of different types. So imagine a function as a magical one-way tube: you put one thing in the hole labeled in and get something else from another hole named out, possibly of entirely different form. Like, you put a tomato in a tube and get a machine gun from the opposite end. But remember, functions are one-way, you can't put anything in the function's out hole.
a :: Int -> Int -- take a thing of type Int and return a thing of type Int
a n = n + 2
b :: Int -> Int
b n = n -- a function can even return the same value
c :: Int -> Bool -- an output can be of different type than input
c n = n `mod` 3 == 0
Haskell supports higher-order functions and is curried by default. This means that there are no multiple-argument functions in Haskell, every function accepts only 1 argument, but sometimes they can return functions. Haskell tubes always has one and only one hole in, but sometimes tubes pop tubes from the hole out! There are even tubes, in which you can put other tubes. Function types are right-associative, which means that a -> b -> c and a -> (b -> c) are the same thing.
:t (+) -- Num a => a -> a -> a
:t (:) -- a -> [a] -> [a]
So what's a function composition? It's when you compose functions into a new function, or when you fuse 2 tubes together, welding 1st's out to 2nd's in, so that whatever you put into the first tube's in hole, falls through and pops out of second tube's out hole.
A composed function then can be imagined as a long tube made out of two shorter tubes welded together. What would be the type of a composed function? Our new tube still has 2 holes in it, one labeled in, another labeled out. But remember, in of our new tube corresponded to in of a first part, and out of our new tube corresponded to out of the second part. So the type will be from whatever was input to 1st function to whatever was output of 2nd function.
:t (:[2,3]) . (+1) -- Num a => a -> [a]
-- why?
:t (+1) -- Num a => a -> a
:t (:[2,3]) -- Num a => a -> [a]
We joint 2 functions together, first's output to second's input, and get a new function that still has one input and one output. And that's exactly what function operator's type says:
:t (.) -- (b -> c) -> (a -> b) -> (a -> c)
Due to a historical accident, function composition goes from right to left, so (*3) . (+2) is a function that first adds 2 to a number, and then multiples by 3 the result. So how to deduce a type of a function composition? You joint the the input of 2nd function to an output of 1st function and throw away the types in between.
a -> b
b -> c becomes
a -> c
See also: [1], [2], [3], [4] for more ideas on how to use function composition and how to reason about function types.
Related
I'm working through "Haskell Programming From First Principles". In the chapter on Folding Lists, exercise 5f,
when I evaluate
foldr const 'a' [1..5]
I get
No instance for (Num Char) arising from the literal ‘1’
However, with
foldl const 'a' [1..5]
I get 'a'.
I get that the folds are lazy, foldr doesn't traverse the spine and foldl does. But even looking at the definitions of foldr and foldl,
foldr f z [] = z
foldr f z (x:xs) = f x (foldr f z xs)
foldl f z [] = z
foldl f z (x:xs) = foldl f (f z x) xs
I can't figure out why it would have this type error. I'm guessing that the compiler is inferring type of x (Num) based on the type of z (Char), but I can't see where it draws the connection, since const or f doesn't require its two arguments to be the same type.
Any thoughts?
ok look at the type of foldr :: (a -> b -> b) -> b -> [a] -> b
Starting from the right you obviously must have a be some instance of Num and Enum (because you use [1..5])
Next you pass in 'a' so you have b ~ Char
Finally you have const for the function - and const is const :: a -> b -> a - note how you now must have a ~ b because you unify
a -> b -> b
a -> b -> a
^^^^^
but this of course would mean that 'a' must be a value of an instance of Num which Char is not ... there is your error (it's complaining about the 1 because starting left instead it's the point where the the problem becomes obvious)
foldl on the other side has foldl :: (b -> a -> b) -> b -> [a] -> b
so now you have again a some instance of Num, b must again be Char but now const just fits in (just switch b and a in the type of const)
foldl and foldr invoke f with a different order of arguments. That is foldr calls const x 'a', but foldl calls const 'a' x. The result of the latter is 'a', which is fine, but the result of the latter is x, which is wrong because x is an Int and the result is supposed to have the same type as the accumulator (Char).
This is a typing problem. The type of foldl is
foldl :: Foldable t => (b -> a -> b) -> b -> t a -> b
and foldr type is:
foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
When you apply foldr to const you get:
foldr const :: Foldable t => b -> t b -> b
Next, you supply 'a' argument, you get
(foldr const 'a') :: Foldable t => t Char -> Char
So, when you pass [1..5] as an argument it will try to unify t Char with (Enum a, Num a) => [a]. Type Char is an instance of Enum class but not Num and it is the reason why you get this error message.
As others have said, the order of arguments in a foldl and foldr are different. Use flip const instead:
> foldr (flip const) 'a' [1..5]
'a'
Hi everyone Haskell newbie here. I'm really confused about how to look at a curried function.
For example, here is a function definition
zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith' _ [] _ = []
zipWith' _ _ [] = []
zipWith' f (x:xs) (y:ys) = f x y : zipWith' f xs ys
When I call
zipWith' (zipWith' (*)) [[1,2,3],[3,5,6]] [[3,2,2],[3,4,5]]
I understand that
zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c]
is equivalent to
zipWith' :: (a -> b -> c) -> ([a] -> ([b] -> [c]))
But what I don't understand is the way/order to look at it. Should we analyse it from left to right? Such as look at the (a -> b -> c) first and then apply the [a] in to the (a -> b -> c) and lastly apply ([b] -> [c]) into (a -> b -> c) -> [a]? Or is it the other way round?
And if you don't understand what I'm asking (sorry :( ), could you just show me how you think when you see this kind of problems? I freak out whenever I see these functions and it usually takes me a while to figure out :P
Curried functions look scarier than they are. One way of thinking about how they work is uncurrying them. For instance:
(++) :: [t] -> [t] -> [t]
Becomes, when uncurried:
(++) :: ([t], [t]) -> [t]
In my eyes, that's all the explanation needed, but here's a more detailed one, that might explain it fully:
A function of type a -> b takes an argument of type a and returns an argument of type b. Remember that a -> b is in itself a concrete type and could take the place of b in the first, making a function of something like a -> (b -> c).
Let's say that a function f is of type a -> b -> c, which is as above. What does that mean? It is a function that returns a function! That is how currying works. The idea of functions returning other functions allows us to partially apply functions, limiting or specifying their traits.
Let's now make our own function specifying that:
func :: a -> [a] -> [a]
func = \elem -> ( \list -> elem : list )
The lambda expressions here have been written in such a way that it should be obvious what the type means: the first lambda (\elem -> ...) constructs the other based on it's input.
Now, let's have a look at zipWith:
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
The first argument to zipWith is a function, which in itself takes two arguments. Not too crazy.
The second argument is a list which can be passed to the function, because of the type variables defined.
The third argument is another list that can be passed to the function after the first.
The return value is a list of the resultant type of the function we passed to zipWith first.
So, let's start with zipping function. It's type is a -> (b -> c). It takes as a parameter of type a, and returns function which transforms b to c. It is isomorphic to it's uncurried equivalence (a, b) -> c, the difference is that we fix first parameter, to get function which now requires only b to return c.
So this is what the zipper's type mean. Now let's move on to zipWith'. It takes as a parameter zipping function (explained above). It returns something of type [a] -> ([b] -> [c]), which is pretty similiar to zipper function. We can say that zipWith' lifts zipping function to work with lists -zipWith' returns a function witch takes a list of as, and returns function which takes list of bs and returns list of cs.
In your example we have zipWith' (*). For simplicity assume that (*) is of type Int -> (Int -> Int) (in reality it is more general - (Num a) => a -> (a -> a). Then we have a ~ b ~ c ~ Int and zipWith' (*) :: [Int] -> ([Int] -> [Int]). Then zipWith' (*) takes as a parameter a list of Ints and returns a function which transforms one list of Ints to another. So it can be used to zip two lists of lists of as. And this is exactly what happens.
In this case it might be helpful to think of the nested lists as (2-dimensional) matrices.
A = ( 1 2 3 ) B = ( 3 2 2 ) --> result = ( 3 4 6 )
( 3 5 6 ) ( 3 4 5 ) ( 9 20 30 )
The outer zip' combines the rows from A and B and the inner zip combines a pair of rows into another row, and so it is clear the result will have the same shape as the inputs.
It also might be helpful to realize that zipWith (*) is just component-wise multiplication of vectors. In fact there is a pattern:
zipWith (*) -- component-wise * on 1-d matrices
zipWith (zipWith (*)) -- component-wise * on 2-d matrices
zipWith (zipWith (zipWith (*))) -- component-wise * on 3-d matrices
...
and of course (*) can be replaced by any binary operation.
Some other list functions and their matrix interpretations:
map - iterate over all of the rows
transpose - transpose rows and columns
concat - flatten the first dimension (i.e. all rows into a single row)
As an exercise you might try implementing matrix multiplication with list operations.
Left to right.
In fact you can see it with 4 different point of view
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
-- take a function, 2 list and returns a list
zipWith (*) :: [a] -> [b] -> [c]
-- new function which takes 2 list and returns a new one
zipWith (*) [1,2,3] :: [b] -> [c]
-- new function which takes a list a produce a new one
zipPwith * [1,2,3] [3,5,6] :: [c]
-- a function without argument, or a value (equivalent in Haskell)
Or right to left :-)
zipPwith * [1,2,3] [3,5,6] :: [c]
-- a function without argument, or a value (equivalent in Haskell)
zipWith (*) [1,2,3] :: [b] -> [c]
-- new function which takes a list a produce a new one
zipWith (*) :: [a] -> [b] -> [c]
-- new function which takes 2 list and returns a new one
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
-- take a function, 2 list and returns a list
In ghci,
:t ((+).(+))
> ((+).(+)) :: (Num (a -> a), Num a) => a -> (a -> a) -> a -> a
but what is this thing? Can any one give me an example of the use of this please?
How can one composite 2 functions that take 2 parameters each?
for example, how does (map.map) :: (a -> b) -> [[a]] -> [[b]] work?
(^.^)
(-.-)
(+.+) (can't help making funny faces out of it. PS: I thought it means to tell the compiler how you feel today)
Num (a -> a) (or e.g. Eq (a -> a)) is basically an indicator for code that doesn't make any sense1, but the compiler nevertheless deduces a (nonsensical) type signature. Usually it turns up when you've forgotten to apply a function to some argument. In this case, obviously (+) needs a "plain number" argument to become a "simple function" to which you can post-compose another such function.
However, (a -> a) is sure enough a valid type of functions that you can also pass on, just not as numbers. For instance, map . (+) is a perfectly good combination:
Prelude> :t map . (+)
map . (+) :: Num b => b -> [b] -> [b]
Prelude> zipWith (map . (+)) [10,20,30] [[1,2],[3,4]]
[[11,12],[23,24]]
because map actually expects a function as its first argument. Similarly,
Prelude> zipWith (map . map) [(+10),(+20),(+30)] [[[1,2],[3,4]],[[5,6]]]
[[[11,12],[13,14]],[[25,26]]]
Here, the right map takes a simple function (like numerical increment) and returns the corresponding list-map function. That function is then fed to the left map resulting in a function that maps nested lists.
1Actually, you can force it to make sense by defining
instance (Num a) => Num (b -> a) where
fromInteger x = const $ fromInteger x
f + g = \x -> f x + g x
Personally, I'm not a fan of this. It confusing, for instance let a = 3 in 4 a produces 4 when most people would expect multiplication to 12.
That won't work. As ghci tells you, you should have an instance of Num (a -> a) in order to use that function, but a -> a obviously isn't a number.
This is because (+) assumes to get two numerical parameters, but with the composition you wrote you gave it a partially applied function instead, the a -> a mentioned in the computed type signature.
Usually, when composing functions which take more than one parameter, you partially apply them first in order to reduce them to functions which take just one parameter, e.g. (+1) . (*2) applied to 3 will result in (3 * 2) + 1 = 7
f . f can make sense for a binary function f; it entirely depends on the signature of f. The key is that partial application of the inner f to its first argument has to give something that is a valid input to the outer f.
For example, with map :: (a -> b) -> [a] -> [b], we can hand-unify map . map:
map :: (a -> b) -> [a] -> [b]
map :: (c -> d) -> [c] -> [d]
. :: (e -> f) -> (f -> g) -> (e -> g)
e === a -> b
f === [a] -> [b]
=== c -> d
c === [a]
d === [b]
g === [c] -> [d] === [[a]] -> [[b]]
map . map :: e -> g
:: (a -> b) -> [[a]] -> [[b]]
So, as expected, map . map takes a transformation a -> b and gives us a transformation from list-of-list-of-a to list-of-list-of-b. We can check this by hand-applying (map . map) f ll:
(map . map) f ll
= map (map f) ll
= map (\l -> map f l) ll
But if we try the same with (+) :: Num a => a -> a -> a, it all goes horribly wrong:
(+) :: Num a => a -> a -> a
(+) :: Num b => b -> b -> b
. :: (c -> d) -> (d -> e) -> (c -> e)
c === a
d === a -> a
=== b
e === b -> b === (a -> a) -> (a -> a)
(+) . (+) :: c -> e
:: (Num a, Num (a -> a)) => a -> (a -> a) -> (a -> a)
So, partial application of the inner + is giving a transformation a -> a, the outer + is then trying to add that transformation to another function which we are expected to supply. Since it doesn't make sense to add transformations, the overall (+) . (+) doesn't make sense either.
g . f means applying f first, then applying g to the result of f, in
other words, it can be rewritten as
\x -> g (f x)
Therefore,
((+) . (+))
can be rewritten as
\x -> (\y -> (x +) + y)
According to the type of (+), in the above lambda abstraction, x needs
having type Num a => a, y having type Num a => Num (a -> a), as inferred
by ghci
(Num a, Num (a -> a)) => a -> (a -> a) -> a -> a
So if we have made a -> a an instance of type class Num a, for example,
here is one way to achieve that
{-# LANGUAGE FlexibleInstances #-}
instance (Num a) => Num ((->) a a) where
a + b = \x -> a x + b x
a * b = \x -> a x * b x
a - b = \x -> a x - b x
negate a = \x -> negate $ a x
abs a = \x -> abs $ a x
signum a = \x -> signum $ a x
fromInteger n = \_x -> fromInteger n
we can use ((+) . (+)) like this
*Main> ((+) . (+)) 1 (+2) 3
9
Because ((+) . (+)) equals
\x -> \y -> (x +) + y
which means ((+) . (+)) 1 (+2) 3 equals
((1 + ) + (+ 2)) 3
according to the definition of (+) in the instance of (a -> a), ((1+) +
(+2)) equals
\x -> (1+x) + (x+2)
So ((1+) + (+2)) 3 equals (1+3) + (3+2), which is 9, as given by ghci.
map . map is similar, as indicated by its type, given by ghci:
(a -> b) -> [[a]] -> [[b]]
the first argument of that function should be a function of type a->b, the
second argument should be a nested list of type [[a]], and that composed
function map . map will apply the first argument to each element of each
list in its second argument, return a nested list of type [[b]]. For
example
*Main> (map . map) (+1) [[1,2], [3,4,5]]
[[2,3],[4,5,6]]
I'm doing some excersises where I have to add a function's type and explain what it does. I'm stuck with this:
phy = uncurry ($)
The type, according to GHCi is phy :: (a -> b, a) -> b. My haskell knowledge is basic so I really have no idea what it does.
Let's spell out the type part systematically. We'll start with the types of uncurry and ($):
uncurry :: (a -> b -> c) -> (a, b) -> c
($) :: (a -> b) -> a -> b
Since the target expression has ($) as the argument of uncurry, let's line up their types to reflect this:
uncurry :: (a -> b -> c) -> (a, b) -> c
($) :: (a -> b) -> a -> b
The whole type of ($) lines up with the first argument type of uncurry, and the argument and result types of ($) line up with those of uncurry's first argument as shown. This is the correspondence:
uncurry's a <==> ($)'s a -> b
uncurry's b <==> ($)'s a
uncurry's c <==> ($)'s b
This is kinda confusing, because the a and b type variables in one type are not the same as in the other (just like the x in plusTwo x = x + 2 is not the same as the x in timesTwo x = x * 2). But we can rewrite the types to help up reason about this. In simple Haskell type signatures like this, any time you see a type variable you can replace all of its occurrences with any other type get a valid type as well. If you pick fresh type variables (type variables that don't appear anywhere in the original), you get an equivalent type (one that can be converted back to the original); if you pick a non-fresh type you get a specialized version of the original that works with a narrower range of types.
But anyway, let's apply this to the type of uncurry::
-- Substitute a ==> x, b ==> y, c ==> z:
uncurry :: (x -> y -> z) -> (x, y) -> z
Let's redo the "line up" using the rewritten type:
uncurry :: (x -> y -> z) -> (x, y) -> z
($) :: (a -> b) -> a -> b
Now it's obvious: x <==> a -> b, y <==> a and z <==> b. Now, substituting uncurry's type variables for their counterpart types in ($), we get:
uncurry :: ((a -> b) -> a -> b) -> (a -> b, a) -> b
($) :: (a -> b) -> a -> b
And finally:
uncurry ($) :: (a -> b, a) -> b
So that's how you figure out the type. How about what it does? Well, the best way to do that in this case is to look at the type and think about it carefully, figuring out what we'd have to write to get a function of that type. Let's rewrite it this way to make it more mysterious:
mystery :: (a -> b, a) -> b
mystery = ...
Since we know mystery is a function of one argument, we can expand this definition to reflect that:
mystery x = ...
We also know that its argument is a pair, so we can expand a bit more:
mystery (x, y) = ...
Since we know that x is a function and y :: a, I like to use f to mean "function" and to name variables the same as their type—it helps me reason about the functions, so let's do that:
mystery (f, a) = ...
Now, what do we put in the right hand side? We know it must be of type b, but we don't know what type b is (it's actually whatever the caller chooses, so we can't know). So we must somehow make a b using our function f :: a -> b and value a :: a. Aha! We can just call the function with the value:
mystery (f, a) = f a
We wrote this function without looking at uncurry ($), but it turns out that it does the same thing as uncurry ($) does, and we can prove it. Let's start with the definitions of uncurry and ($):
uncurry f (a, b) = f a b
f $ a = f a
Now, substituting equals for equals:
uncurry ($) (f, a) = ($) f a -- definition of uncurry, left to right
= f $ a -- Haskell syntax rule
= f a -- definition of ($), left to right
= mystery (f, a) -- definition of mystery, right to left
So one way to attack a type that you don't understand in Haskell is to just try and write some code that has that type. Haskell is different from other languages in that very often this is a better strategy than trying to read the code.
uncurry :: (a -> b -> c) -> (a, b) -> c
($) :: (a -> b) -> a -> b
uncurry ($) :: (a -> b, a) -> b
If you inspect types of uncurry and $ and its description:
uncurry converts a curried function to a function on pairs.
All it does is it takes a function (a -> b -> c) and returns a function that takes the parameters as a tuple.
So phy does the same thing as $, but instead of f $ x or ($) f x you call it like phy (f, x).
The other two answers are fine. I just have a slightly different take on it.
uncurry :: (a -> b -> c) -> (a, b) -> c
($) :: (a -> b) -> a -> b
Since the "->" in type signatures associates to the right, I can equivalently write these two type signatures like this:
uncurry :: (a -> b -> c) -> ((a, b) -> c)
($) :: (a -> b) -> (a -> b)
uncurry takes an arbitrary function of two inputs and changes it into a funciton of one argument where that argument is a tuple of the original two arguments.
($) takes a simple one-argument function and turns it into...itself. Its only effect is syntactical. f $ is equivalent to f.
(Make sure you understand higher-order functions and currying, read Learn You a Haskell chapter on higher-order functions, then read difference between . (dot) and $ (dollar sign) and function composition (.) and function application ($) idioms)
($) is just a function application, f $ x is equivalent to f x. But that's good, because we can use explicit function application, for example:
map ($2) $ map ($3) [(+), (-), (*), (**)] -- returns [5.0,1.0,6.0,9.0]
which is equivalent to:
map (($2) . ($3)) [(+), (-), (*), (**)] -- returns [5.0,1.0,6.0,9.0]
Check the type of ($): ($) :: (a -> b) -> a -> b. You know that type declarations are right-associative, therfore the type of ($) can also be written as (a -> b) -> (a -> b). Wait a second, what's that? A function that receives an unary function and returns an unary function of the same type? This looks like a particular version of an identity function id :: a -> a. Ok, some types first:
($) :: (a -> b) -> a -> b
id :: a -> a
uncurry :: (a -> b -> c) -> (a, b) -> c
uncurry ($) :: (b -> c, b) -> c
uncurry id :: (b -> c, b) -> c
When coding Haskell, always look at types, they give you lots of information before you even look at the code. So, what's a ($)? It's a function of 2 arguments. What's an uncurry? It's a function of 2 arguments too, the first being a function of 2 arguments. So uncurry ($) should typecheck, because 1st argument of uncurry should be a function of 2 arguments, which ($) is. Now try to guess the type of uncurry ($). If ($)'s type is (a -> b) -> a -> b, substitute it for (a -> b -> c): a becomes (a -> b), b becomes a, c becomes b, therefore, uncurry ($) returns a function of type ((a -> b), a) -> b. Or (b -> c, b) -> c as above, which is the same thing. So what does that type tells us? uncurry ($) accepts a tuple (function, value). Now try to guess what's it do from the type alone.
Now, before the answer, an interlude. Haskell is so strongly typed, that it forbids to return a value of a concrete type, if the type declaration has a type variable as a return value type. So if you have a function with a type a -> b, you can't return String. This makes sense, because if your function's type was a -> a and you always returned String, how would user be able to pass a value of any other type? You should either have a type String -> String or have a type a -> a and return a value that depends solely on an input variable. But this restriction also means that it is impossible to write a function for certain types. There is no function with type a -> b, because no one knows, what concrete type should be instead of b. Or [a] -> a, you know that this function can't be total, because user can pass an empty list, and what would the function return in that case? Type a should depend on a type inside the list, but the list has no “inside”, its empty, so you don't know what is the type of elements inside empty list. This restriction allows only for a very narrow elbow room for possible functions under a certain type, and this is why you get so much information about a function's possible behavior just by reading the type.
uncurry ($) returns something of type c, but it's a type variable, not a concrete type, so its value depends on something that is also of type c. And we see from type declaration that the function in the tuple returns values of type c. And the same function asks for a value of type b, which can only be found in the same tuple. There are no concrete types nor typeclasses, so the only thing uncurry ($) can do is to take the snd of a tuple, put it as an argument in function in fst of a tuple, return whatever it returns:
uncurry ($) ((+2), 2) -- 4
uncurry ($) (head, [1,2,3]) -- 1
uncurry ($) (map (+1), [1,2,3]) -- [2,3,4]
There is a cute program djinn that generates Haskell programs based on types. Play with it to see that our type guesses of uncurry ($)'s functionality is correct:
Djinn> f ? a -> a
f :: a -> a
f a = a
Djinn> f ? a -> b
-- f cannot be realized.
Djinn> f ? (b -> c, b) -> c
f :: (b -> c, b) -> c
f (a, b) = a b
This shows, also, that fst and snd are the only functions that can have their respective types:
Djinn> f ? (a, b) -> a
f :: (a, b) -> a
f (a, _) = a
Djinn> f ? (a, b) -> b
f :: (a, b) -> b
f (_, a) = a
Currently I am learning Haskell. We have to determine the most general types for given functions, but I do not get it yet. How does the interpreter determine the most general type of the function, especially lambda expressions? What is a secure way to determine the most general type manually?
tx2 = (\x y z -> y.z.z)
tx2::a->(a->b)->(a->a)->b -- my guess
tx2 :: a -> (b -> c) -> (b -> b) -> b -> c -- interpreter solution
If the first variable (a) is applied to expression z, then z must take a as an input parameter, but it consumes b instead in (b->b). y consumes b and generates c, so the final result must be c. But why is b (as intermediate result?) contained in the type? And if so, why is it not a -> (b -> c) -> (b -> b) -> b-> b -> c ?
tm2 = (\i -> [sum,product]!!i)
tm2:: Int->[(Integer->Integer->Integer)]->(Integer->Integer->Integer) -- my guess
\i -> [sum,product] !! i :: Num a => Int -> [a] -> a -- interpreter with direct input
tm2 :: Int -> [Integer] -> Integer -- interpreter with :info tm2
So the interpreter has more detailed information about the type if tm2 is in coded in the script, right? So the type in the second line is the result of the expression. Why are only Integers accepted in line 2, not Float for example?
tp2 = (\x -> \y -> (x.y.x))
tp2::(a->b)->((a->b)->a)->a -- my guess
tp2 :: (a -> b) -> (b -> a) -> a -> b -- interpreter solution
Why do I have to include the intermediate result a here in the type? Why is \y not represented using (a->b)->a like in tf2 below?
tf2 = (\x -> \y -> (x (y x), x, y))
tf2::(a->b)->((a->b)->a)->(a,a->b,(a->b)->a) -- solution
tg2 = (\x y z a -> y(z(z(a))));
tg2::a->(b->c)->(b->b)->b->c -- solution
Here we do not need any intermediate results? We write down the types of the params and then the type of the result?
tx2 = (\x y z -> y.z.z)
tx2 takes three arguments (for the sake of discussion, I ignore currying), ignores the first and composes the second and twice the third. So the first argument can have any type, the second and third must have function type, say
y :: ay -> ry
z :: az -> rz
Now the result of the first application of z becomes the argument of the second application of z, so the result type of z, rz must be the argument type of z, az, let us call that b, so
z :: b -> b
Then the result of the z application becomes the argument of y, so the argument type of y must be the same type as the result type of z, but the result type of y is completely unconstrained by the expression, hence
y :: b -> c
and y . z . z :: b -> c, thus
tx2 :: a -> (b -> c) -> (b -> b) -> (b -> c)
Then the next,
tm2 = (\i -> [sum,product]!!i)
Now, sum and product are functions from the Prelude, both have the type Num a => [a] -> a, thus
Prelude> :t [sum,product]
[sum,product] :: Num a => [[a] -> a]
Since (!!) :: [e] -> Int -> e, given a list xs of type [e], the expression \i -> xs !! i has type Int -> e. Thus the inferred type of tm2 = \i -> [sum,product] !! i is
tm2 :: Num a => Int -> ([a] -> a)
But, tm2 is bound by a simple pattern binding without type signature, so the monomorphism restriction kicks in, and the type of tm2 has to be monomorphised. By the defaulting rules, a Num a constraint is resolved by instantiating the type variable a with Integer (unless an explicit default declaration says otherwise), so you get
Prelude> let tm2 = \i -> [sum,product] !! i
Prelude> :t tm2
tm2 :: Int -> [Integer] -> Integer
unless you disable the monomorphism restriction (:set -XNoMonomorphismRestriction).
tp2 = (\x -> \y -> (x.y.x))
The result of the first application of x becomes the argument of y, hence the result type of x must be the same as the argument type of y. Then the result of the application of y becomes the argument of the second application of x, so the result type of y must be the argument type of ´x`, altogether
tp2 :: (a -> b) -> (b -> a) -> (a -> b)
Then in
tf2 = (\x -> \y -> (x (y x), x, y))
the only interesting part is the first component of the result, x (y x), so x is applied to the result of an application of y, hence x must have a function type
x :: a -> b
and y must have result type a. But y is applied to x, so its argument type must be the type of x,
y :: (a -> b) -> a
and
tf2 :: (a -> b) -> ((a -> b) -> a) -> (b, a -> b, (a -> b) -> a)
Finally
tg2 = (\x y z a -> y(z(z(a))))
which is, by the way, exactly the same as tx2, only eta-expanded by providing one more argument in the lambda. Therefore the derivation of the type is the same too.
I think many of your doubts about function types in Haskell would get an answer by reading something about "currying", on wikipedia for example.
The inferred types of the functions you wrote are like that because Haskell works with curried functions, i.e. functions with only an input parameter that may return (depending on the definition) other functions.
So, when you write a function like mySum x y = x + y its type is a -> a -> a, more easily read by making explicit the right associativity of ->: a -> (a -> a). See? The extra return types you get aren't "intermediate results" as you call them.
You do not take two parameters and return the result, an a. You take one parameter, x, fix its value in the expression x + y, and return the function a -> a that takes a parameter (y this time) and sums it to the y you previously fixed.
Answering point by point to your question would be kinda long so I'll pass, but this is the reasoning you have to do in order to check for the types of your functions.
I'm not sure about difference between the inferred types by the interpreter for your second question on tm2.