功能组成类型不排队

时间:2014-12-09 05:17:42

标签: haskell

我在此代码中列出的类型有点麻烦:

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

我的印象是,由于distance会返回Floating个变量,我可以将其输入((**) 2.0)。有没有解决这个问题,同时保持代码相对优雅?

我得到的错误是:

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':

1 个答案:

答案 0 :(得分:2)

问题是distance有两个参数,你需要一个与两个一起使用的合成运算符。 There's a package那里定义了方便的,但它很容易记住:

infixr 9 .:
(.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
(.:) = (.).(.)

现在你应该能够做到

gravitation = (negate 1 **) . (2 **) .: distance

infixr 9很重要,它将.:运算符的固定性设置为与.运算符相同。我总是很快用:i (.)在GHCi中检查这个,因为谁有时间记住这些东西?每当我看到这个问题时,我总是想指出这个运算符使用fmap有一个更通用的形式:

(.:) :: (Functor f, Functor g) => (a -> b) -> f (g a) -> f (g b)
(.:) = fmap fmap fmap

从技术上讲,这些fmap中的第一个用于函数仿函数,因此它也与fmap . fmap相同。这个更通用的形式简单地fmap是一个2层深的函数,并且在函数是你的函子的情况下,它结果是这个方便的组合运算符。但它有其他用途:

(+1) .: [Just 1, Nothing, Just 3]
[Just 2, Nothing, Just 4]

同样,它只是将(+1)两个仿函数层应用于深层,这就是它。