反转类型族

时间:2014-10-24 05:16:06

标签: haskell type-families

我有一个“线性”类型的家庭,即

形式
type family Foo a
type instance Foo T1 = T2
type instance Foo T2 = T3
...
type instance Foo T9 = T10

在我的特定用例中,定义“反向”族FooRev然后强制执行约束(FooRev (Foo x) ~ x)非常方便:

type family FooRev a
type instance FooRev T10 = T9
type instance FooRev T9 = T8
...
type instance FooRev T2 = T1

反向族允许GHC推断出许多类型,否则这些类型由于非注入性而会模糊不清。它与here提出的想法基本相同。这个解决方案工作得很好,但是通过列出所有案例来定义“反向”类型系列是令人烦恼的,编程的,并且容易出错。是否有更通用的方法来定义线性族的反转,例如Foo

2 个答案:

答案 0 :(得分:5)

我认为FunctionalDependencies应该是您案例的最佳解决方案:

{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}

class Foo a b | a -> b, b -> a
instance Foo T1 T2
instance Foo T2 T3
...

现在每个b都可以从a推断,反之亦然。

答案 1 :(得分:5)

我决定尝试@ chi的想法,但最终我想出了一些更简单的东西。

{-# LANGUAGE TypeOperators, DataKinds, TypeFamilies #-}

type family NextElt (a :: *) (xs :: [*]) where
  NextElt a (a ': b ': cs) = b
  NextElt a (b ': c ': cs) = NextElt a (c ': cs)

type family PrevElt (a :: *) (xs :: [*]) :: * where
  PrevElt a (b ': a ': xs) = b
  PrevElt a (b ': c ': xs) = PrevElt a (c ': xs)

data T1
data T2
data T3
type TSeq = '[T1, T2, T3]

type NextRp a = NextElt a TSeq
type PrevRp a = PrevElt a TSeq

使用类型列表来表示类型的线性序列允许我表达关系,而无需每次写入两次类型(这是使用类型族实例或类的实例所必需的)。上面的类型系列使用滑动窗口方法来搜索具有前一个或下一个元素的列表中的元素。这些类型系列是通用的(并且可以使用Data.Type.Equality扩展为非*种类。)