【发布时间】:2016-07-27 20:59:13
【问题描述】:
在一个项目中,我经常需要总结列表和哈希。我最终得到了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’
我真的不明白这个错误。非常欢迎任何帮助。
【问题讨论】:
-
Haskell 在查看约束之前执行实例解析,因此您实际上定义了一个实例
forall f b. Num (f b),这是非常通用的,可能不是您想要的。只有在选择了一个实例之后才会检查约束。 -
是的,这肯定不是我想要的。知道如何解决这个问题吗?
-
这可能是您所要求的,但在这里交付您所要求的,需要付出高昂的代价。在任何类型的 * -> * 上没有其他 Num 实例。更好的机制是创建一个
newtype ANum f a = ANum (f a),然后将您的实例烘焙到新类型中。这样你就不会在任何人都会写的 * -> * 类型的东西上与所有其他可能的 num 实例发生冲突,并且用户可以将他们的数据包装成与你的用例兼容的形式。考虑newtype Poly a = Poly [a]它将与您的实例重叠,但肯定不应该使用 []` 的应用程序来完成!
标签: haskell