使Data.Lazy.Map成为Num的一个实例

时间:2016-07-27 20:59:13

标签: haskell

在一个项目中,我经常需要总结列表和哈希。我最后得到了以下两个Num实例

的声明
{-# LANGUAGE FlexibleInstances #-}
module Tweak where
import Data.List
import qualified Data.Map.Lazy as M
import Control.Applicative

instance (Applicative f, Num b) => Num (f b) where
  negate      = fmap negate
  (+)         = liftA2 (+)
  (*)         = liftA2 (*)
  fromInteger = pure . fromInteger
  abs         = fmap abs
  signum      = fmap signum

instance (Ord k, Num b) => Num(M.Map k b) where
  negate      = fmap negate
  (+)         = M.unionWith (+)
  (*)         = M.unionWith (*)
  fromInteger = undefined
  abs         = fmap abs
  signum      = fmap signum

在ghci中运行此(M.fromList [(2 :: Int, 3 :: Int)]) + (M.fromList [(1, 2)]) 会产生以下错误

<interactive>:2:37:
    Overlapping instances for Num (M.Map Int Int)
      arising from a use of ‘+’
    Matching instances:
      instance (Ord k, Num b) => Num (M.Map k b)
        -- Defined at Tweak.hs:15:10
      instance (Applicative f, Num b) => Num (f b)
        -- Defined at Tweak.hs:7:10
    In the expression:
      (M.fromList [(2 :: Int, 3 :: Int)]) + (M.fromList [(1, 2)])
    In an equation for ‘it’:
        it = (M.fromList [(2 :: Int, 3 :: Int)]) + (M.fromList [(1, 2)])

如果我理解错误Haskell认为M.Map IntApplicative的实例。似乎并非如此。 :i M.Map Int产生

type role M.Map nominal representational
data M.Map k a
  = containers-0.5.6.2:Data.Map.Base.Bin {-# UNPACK #-}containers-0.5.6.2:Data.Map.Base.Size
                                         !k
                                         a
                                         !(M.Map k a)
                                         !(M.Map k a)
  | containers-0.5.6.2:Data.Map.Base.Tip
    -- Defined in ‘containers-0.5.6.2:Data.Map.Base’
instance (Ord k, Num b) => Num (M.Map k b)
  -- Defined at Tweak.hs:15:10
instance (Eq k, Eq a) => Eq (M.Map k a)
  -- Defined in ‘containers-0.5.6.2:Data.Map.Base’
instance Functor (M.Map k)
  -- Defined in ‘containers-0.5.6.2:Data.Map.Base’
instance (Ord k, Ord v) => Ord (M.Map k v)
  -- Defined in ‘containers-0.5.6.2:Data.Map.Base’
instance (Ord k, Read k, Read e) => Read (M.Map k e)
  -- Defined in ‘containers-0.5.6.2:Data.Map.Base’
instance (Show k, Show a) => Show (M.Map k a)
  -- Defined in ‘containers-0.5.6.2:Data.Map.Base’
instance Foldable (M.Map k)
  -- Defined in ‘containers-0.5.6.2:Data.Map.Base’
instance Traversable (M.Map k)
  -- Defined in ‘containers-0.5.6.2:Data.Map.Base’
instance Ord k => Monoid (M.Map k v)
  -- Defined in ‘containers-0.5.6.2:Data.Map.Base’
data Ability = ... | Int | ...  -- Defined at Character.hs:60:41

data Int = GHC.Types.I# GHC.Prim.Int#   -- Defined in ‘GHC.Types’
instance Bounded Int -- Defined in ‘GHC.Enum’
instance Enum Int -- Defined in ‘GHC.Enum’
instance Eq Int -- Defined in ‘GHC.Classes’
instance Integral Int -- Defined in ‘GHC.Real’
instance Num Int -- Defined in ‘GHC.Num’
instance Ord Int -- Defined in ‘GHC.Classes’
instance Read Int -- Defined in ‘GHC.Read’
instance Real Int -- Defined in ‘GHC.Real’
instance Show Int -- Defined in ‘GHC.Show’

我真的不明白这个错误。我们非常欢迎任何帮助。

1 个答案:

答案 0 :(得分:3)

为什么人们总是认为定义这种实例是个好主意?

类型类都是关于允许安全地编写泛型函数。这可以通过指定某些方法并明确它们应该如何一起玩来实现。比如,monad法则等。
Num基本上代表了ring的数学概念,加上一种有点半心半意的方法来定义metric。这仅适用于您实际将其限制为数字类型的情况。一般数字集合与您的Applicative实例一样,可能根本不遵守这些法律。

所以,请远离这种情况。我怀疑你尝试使用它们的方式无论如何都与通用算法无关......如果你只想为M.unionWith (+)设置一个consise运算符名称,那么你就是在滥用类型类。你可以定义一个简单的单态运算符(+<)(或者,至少,不是ad-hoc-polymorphic - 如果它适用于(Num n, Ord k) => M.Map k n它可能没问题)或类似的东西。

如果您认为确实需要类多态,那么请寻找合适的类! Additive可能有意义。不料,it actually has an instance for Map已经......

我应该提到第三种可能性,实际上我通常建议在某种意义上更接近Num实例的想法:VectorSpace class。与Num类似,这实际上是一类价值类型,而不是仿函数,但与Num不同,它没有abs等模糊。

Additive不同,此软件包不附带Map个实例,但只有a dedicated type只有tha而且函数实例是标准的,因此,使用vector-space的{{事实上,你可以将^+^值加起来。