I am trying to compose a function of type (Floating a) => a -> a -> a
with a function of type (Floating a) => a -> a
to obtain a fun
Your problem has nothing to do with Floating
, but with the fact that you want to compose a function with two arguments and a function with one argument in a way that doesn't typecheck. I'll give you an example in terms of a composed function reverse . foldr (:) []
.
reverse . foldr (:) []
has the type [a] -> [a]
and works as expected: it returns a reversed list (foldr (:) []
is essentially id for lists).
However reverse . foldr (:)
doesn't type check. Why?
Let's review some types:
reverse :: [a] -> [a]
foldr (:) :: [a] -> [a] -> [a]
foldr (:) [] :: [a] -> [a]
(.) :: (b -> c) -> (a -> b) -> a -> c
reverse . foldr (:) []
typechecks, because (.)
instantiates to:
(.) :: ([a] -> [a]) -> ([a] -> [a]) -> [a] -> [a]
In other words, in type annotation for (.)
:
a
becomes [a]
b
becomes [a]
c
becomes [a]
So reverse . foldr (:) []
has the type [a] -> [a]
.
reverse . foldr (:)
doesn't type check though, because:
foldr (:) :: [a] -> [a] -> [a]
Being the right operant of (.)
, it would instantiate its type from a -> b
to [a] -> ([a] -> [a])
. That is, in:
(b -> c) -> (a -> b) -> a -> c
a
would be replaced with [a]
b
would be replaced with [a] -> [a]
.If type of foldr (:)
was a -> b
, the type of (. foldr (:))
would be:
(b -> c) -> a -> c`
(foldr (:)
is applied as a right operant to (.)
).
But because type of foldr (:)
is [a] -> ([a] -> [a])
, the type of (. foldr (:))
is:
(([a] -> [a]) -> c) -> [a] -> c
reverse . foldr (:)
doesn't type check, because reverse
has the type [a] -> [a]
, not ([a] -> [a]) -> c
!
When people first learn function composition in Haskell, they learn that when you have the last argument of function at the right-most of the function body, you can drop it both from arguments and from the body, replacing or parentheses (or dollar-signs) with dots. In other words, the below 4 function definitions are equivalent:
f a x xs = g ( h a ( i x xs))
f a x xs = g $ h a $ i x xs
f a x xs = g . h a . i x $ xs
f a x = g . h a . i x
So people get an intuition that says “I just remove the right-most local variable from the body and from the arguments”, but this intuition is faulty, because once you removed xs
,
f a x = g . h a . i x
f a = g . h a . i
are not equivalent! You should understand when function composition typechecks and when it doesn't. If the above 2 were equivalent, then it would mean that the below 2 are also equivalent:
f a x xs = g . h a . i x $ xs
f a x xs = g . h a . i $ x xs
which makes no sense, because x
is not a function with xs
as a parameter. x
is a parameter to function i
, and xs
is a parameter to function (i x)
.
There is a trick to make a function with 2 parameters point-free. And that is to use an “owl” operator:
f a x xs = g . h a . i x xs
f a = g . h a .: i
where (.:) = (.).(.)
The above two function definitions are equivalent. Read more on “owl” operator.
Haskell programming becomes much easier and straightforward, once you understand functions, types, partial application and currying, function composition and dollar-operator. To nail these concepts, read the following StackOverflow answers:
Read also: