Tombert
Tombert

Reputation: 530

Function composition types not lining up

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

Answers (1)

bheklilr
bheklilr

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 fmaps is for the function functor, so it's the same as fmap . fmap as well. This more general form simply fmaps 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

Related Questions