在一个项目中,我经常需要总结列表和哈希。我最后得到了以下两个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 Int
是Applicative
的实例。似乎并非如此。 :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’
我真的不明白这个错误。我们非常欢迎任何帮助。
答案 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的{{事实上,你可以将^+^
值加起来。