【问题标题】:How to define these overlapping instances with type families?如何用类型族定义这些重叠的实例?
【发布时间】: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 就是这样:它为FloatDouble、...CIntMax 提供单独的“原始实例”,并且只使复合实例具有多态性。
  • @leftaroundabout,是的,那是我开始分叉的时候,心里想“这是很多重复。让我们改进吧。”
  • @Turion:是的,这真的是非常机械的重复,你可以用一个简单的 CPP 宏来实现它。我同意这不太好......基本上我们想要的是一个适用于封闭集类型的单个实例。 See this post,也许你可以用它来更好地实现实例。

标签: haskell typeclass type-families


【解决方案1】:

在 GHC(相对)新的封闭型家族的帮助下,我认为我们可以成功。以下所有类型检查:

type family Groundfield v where
    Groundfield (a,a) = a
    Groundfield a = a

class Fractional (Groundfield v) => VectorSpace v where
    (^+^)      :: v -> v -> v
    zeroVector :: v
    (^*)       :: v -> Groundfield v -> v

instance (Fractional (Groundfield a), Num a, a ~ Groundfield a) => VectorSpace a where
    (^+^)              = (+)
    zeroVector         = 0
    (^*)               = (*)

instance Fractional a => VectorSpace (a,a) where
    (a, b) ^+^ (c, d)      = (a + c, b + d)
    zeroVector             = (0,0)
    (a, b) ^* c            = (a * c, b * c)

【讨论】:

  • 哇!但是如果这两个实例位于不同的模块中呢?
  • 封闭类型族强制所有 Groundfield 实例一次声明。但是 VectorSpace 的实例可以在任何地方声明。
猜你喜欢
  • 1970-01-01
  • 2014-10-02
  • 2018-02-22
  • 1970-01-01
  • 2016-01-06
  • 2021-03-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多