Reputation: 530
I'm having a bit of trouble with my types lining up in this code:
distance :: (Floating a) => (a, a) -> (a, a) -> a
distance (x1, y1) (x2, y2) = sqrt $ (x2 - x1)**2 + (y2 - y1)**2
gravitation :: (Num a, Floating a) => (a,a) -> (a,a) -> a
gravitation = ((**) (-1.0)) . ((**) 2.0) . distance
I was under the impression that since distance
returns a Floating
variables, I could pipe it into the ((**) 2.0)
. Is there a fix for this whilst keeping the code relatively elegant?
The error I get is:
Main.hs:13:16:
Could not deduce (Floating ((a, a) -> a))
arising from a use of `**'
from the context (Num a, Floating a)
bound by the type signature for
gravitation :: (Num a, Floating a) => (a, a) -> (a, a) -> a
at Main.hs:12:16-57
Possible fix:
add an instance declaration for (Floating ((a, a) -> a))
In the first argument of `(.)', namely `((**) (- 1.0))'
In the expression: ((**) (- 1.0)) . ((**) 2.0) . distance
In an equation for `gravitation':
Upvotes: 1
Views: 57
Reputation: 54058
The problem is that distance
takes two arguments, you need a composition operator that works with two. There's a package out there that has handy ones defined, but it's pretty easy to remember in a pinch:
infixr 9 .:
(.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
(.:) = (.).(.)
Now you should be able to do
gravitation = (negate 1 **) . (2 **) .: distance
The infixr 9
is important, it sets the fixity of the .:
operator to be the same as the .
operator. I always just quickly check this in GHCi with :i (.)
because who has time to remember these things? Whenever I see this problem, I always like to point out that this operator has a more general form using fmap
:
(.:) :: (Functor f, Functor g) => (a -> b) -> f (g a) -> f (g b)
(.:) = fmap fmap fmap
Technically, the first of those fmap
s is for the function functor, so it's the same as fmap . fmap
as well. This more general form simply fmap
s a function 2 layers deep, and in the case of functions being your functors it turns out to be this handy composition operator. It has other uses, though:
(+1) .: [Just 1, Nothing, Just 3]
[Just 2, Nothing, Just 4]
Again, it just applies (+1)
two functor layers deep, that's it.
Upvotes: 2