【发布时间】:2016-03-12 09:38:35
【问题描述】:
假设我想定义一个向量空间类型类。我做了以下事情(受 Yampa 启发):
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
class Fractional (Groundfield v) => VectorSpace v where
type Groundfield v
(^+^) :: v -> v -> v
zeroVector :: v
(^*) :: v -> Groundfield v -> v
效果很好。现在介绍一些常见的情况。当然,分数元组是一个向量空间:
instance Fractional a => VectorSpace (a,a) where
type Groundfield (a,a) = a
(a, b) ^+^ (c, d) = (a + c, b + d)
zeroVector = (0,0)
(a, b) ^* c = (a * c, b * c)
或者更简单,小数是其自身之上的向量空间:
instance Fractional a => VectorSpace a where
type Groundfield a = a
(^+^) = (+)
zeroVector = 0
(^*) = (*)
每个实例本身都是完全有效的。但是如果我把它们放在同一个模块中,我就会遇到这个问题:
VectorSpielwiese.hs:15:10:
Conflicting family instance declarations:
Groundfield (a, a) -- Defined at VectorSpielwiese.hs:15:10
Groundfield a -- Defined at VectorSpielwiese.hs:21:10
我意识到在第二个实例定义中匹配 a 也会捕获一个元组模式。但是如果我之前为(a,a) 编写实例定义,我会期望我已经匹配了该模式。显然不是!我怎样才能做到这一点?
【问题讨论】:
-
您不能,除非您将第二个实例更改为具有新类型包装器,或者将
GroundField更改为数据系列。我怀疑这些中的任何一个都是您正在寻找的。在这种情况下通常采用的方法是有一个对应于这个“平凡”向量空间的“默认”实现,然后你只需写instance VectorSpace Float; instance VectorSpace Double;...。为此,您可能需要DefaultSignatures。 -
the standard vector space class does it 就是这样:它为
Float、Double、...CIntMax提供单独的“原始实例”,并且只使复合实例具有多态性。 -
@leftaroundabout,是的,那是我开始分叉的时候,心里想“这是很多重复。让我们改进吧。”
-
@Turion:是的,这真的是非常机械的重复,你可以用一个简单的 CPP 宏来实现它。我同意这不太好......基本上我们想要的是一个适用于封闭集类型的单个实例。 See this post,也许你可以用它来更好地实现实例。
标签: haskell typeclass type-families