接下来是我正在尝试做的非常简化的版本。
假设我想创建一个通用的差异操作,它可以接受不同类型的操作数。
class Diff a b c where
diff :: a -> b -> c
自然地,我们可以将此操作应用于数字。
instance Num a ⇒ Diff a a a where
diff = (-)
但不仅是数字。如果我们说两个时间点,那么它们之间的时间差就是一个时间间隔。
newtype TimePoint = TP Integer deriving Show -- seconds since epoch
newtype TimeInterval = TI Integer deriving Show -- in seconds
instance Diff TimePoint TimePoint TimeInterval where
diff (Tp x) (Tp y) = TI (x-y)
一切都很好。除非我尝试在GHCi中测试diff
,否则我会得到:
*Example λ diff 5 3
<interactive>:1:1: error:
• Could not deduce (Diff a0 b0 c)
from the context: (Diff a b c, Num a, Num b)
bound by the inferred type for ‘it’:
forall a b c. (Diff a b c, Num a, Num b) => c
at <interactive>:1:1-8
The type variables ‘a0’, ‘b0’ are ambiguous
• In the ambiguity check for the inferred type for ‘it’
To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
When checking the inferred type
it :: forall a b c. (Diff a b c, Num a, Num b) => c
*Example λ
因此,我必须在编译器类型应该是“显而易见的”的地方写类型签名。
让我们尝试一些帮助。
class Diff a b c | a b -> c where
diff ∷ a -> b -> c
它现在应该能够确定结果的类型!不幸的是,这无法编译:
[1 of 1] Compiling Example ( Example.hs, interpreted )
Example.hs:8:10: error:
Functional dependencies conflict between instance declarations:
instance Num a => Diff a a a -- Defined at Example.hs:8:10
instance Num a => Diff (TimePoint a) (TimePoint a) (TimeInterval a)
-- Defined at Example.hs:14:10
|
8 | instance Num a => Diff a a a where
| ^^^^^^^^^^^^^^^^^^^
Failed, no modules loaded.
Prelude GOA λ
顺便说一句,我也尝试过使用关联类型的家庭来代替肱二头肌,其结果可预测地相似。
现在我完全理解为什么会这样。 Diff a a a
和Diff (TimePoint a) (TimePoint a) (TimeInterval a)
有两个实例,它们不能与适当的Fundep共存。问题是,如何解决此问题?在新类型中包装数字不是可行的解决方案,我需要能够编写diff 5 3
和diff time1 time2
,并且这些表达式的类型应从操作数中推导。
我知道我可以为Diff Int Int Int
和Diff Double Double Double
和Diff Rational Rational Rational
定义单独的实例,但这不是理想的解决方案,因为可以定义Num
的新实例并该代码必须处理它们,而不必为每个定义另外的Diff
实例。
一个最小的完整示例如下:
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances #-}
module Example where
class Diff a b c | a b -> c where
diff :: a -> b -> c
instance Num a => Diff a a a where
diff = (-)
data TimePoint a = TP a deriving Show
data TimeInterval a = TI a deriving Show
instance Num a => Diff (TimePoint a) (TimePoint a) (TimeInterval a) where
diff (TP x) (TP y) = TI (x - y)
答案 0 :(得分:1)
问题在于Diff (TimePoint a) (TimePoint a)
恰好是Diff a a
的特例。您可能会认为“不是因为Num a
约束”,但请记住,您永远不能证明类型不是某个类的实例,因为该实例以后可能仍会添加
解决方案是不定义Diff a a a
实例。而是分别定义Diff Int Int Int
和Diff Double Double Double
和Diff Rational Rational Rational
。
答案 1 :(得分:1)
您可以尝试使用常见的技巧来避免@leftaroundabout在其答案中描述的头部匹配问题
authorizedPost() {
getItem('token', this.setToken.bind(this)).then(
()=>this.post()
);
}
这需要instance {-# OVERLAPPABLE #-} (a ~ b, a ~ c, Num a) => Diff a b c where
diff = (-)
和UndecidableInstances
来启用统一约束,除非最终具体键入TypeFamilies
的结果,否则它将无法工作,因此需要进行某种程度的推断,例如在GHCi中是不可能的。