推广类参数

时间:2015-09-23 06:13:41

标签: haskell typeclass type-families

让我们说我上课了:

class C a b t where
  f :: (a, b) -> (t a, t b)

现在有了这个定义,我可以定义实例:

(a,b) -> (Maybe a, Maybe b)
(a,b) -> ([a], [b])

但不是(据我所知):

(a,b) -> (a,b)
(a,b) -> ((a, a), (b, b))

我可以改为改变我的类定义:

type family T a b x

class C a b where
  f :: (a, b) -> (T a b a, T a b b)

这将允许我执行上述操作,但之后我只能为每个fa声明一个b

基本上我希望能够在原始定义中将类型族传递为t,如果已知,则由类型检查器隐式解析。我不想让它成为f :: (a, b) -> (c, d)因为我想要保持ab对它们做同样的事情的不变量,所以swap . ff . swap的类型相同。我想我可能需要injective type families(来自GHC 8.0),但我不确定。但也许还有另一种方式吗?

1 个答案:

答案 0 :(得分:5)

根据您的实际需要,这可能会也可能不会回答您的问题。

标准解决方案是使用newtype来定义新实例。 E.g。

newtype Pair a = Pair (a, a)
instance C a b Pair where
   f = ...

但是,这会导致f具有类型

f :: (a, b) -> (Pair a, Pair b)

而不是想要的

f :: (a, b) -> ((a, a), (b, b))

后者可以以无聊的方式恢复:

f' :: (a, b) -> ((a, a), (b, b))
f' p = case f p of (Pair x, Pair y) -> (x, y)

现在,编写诸如f'之类的“适配器”功能感觉多余:毕竟我们只是删除一个newtype包装器,它不会改变运行时表示。更糟糕的是,这可能效率低下:考虑将[Pair a]转换为[(a, a)]。要做到这一点,我们需要映射整个列表,所以我们付O(n)做基本上没有

可以使用safe coercions

构建有效的替代方案
import Data.Coerce

f'' :: forall a b. (a, b) -> ((a, a), (b, b))
f'' = coerce (f :: (a, b) -> (Pair a, Pair b))

这允许使用newtype来驱动实例选择,但在它们挡住时将其删除。

现在,如果您的目标确实是定义没有newtype的新实例,那么这无济于事。如果您想要一种简单的方法从实例方法中删除newtype包装器,它可以提供帮助。