在类型级别上进行过滤

时间:2018-08-30 14:05:31

标签: haskell

考虑一下,我有类型标记:

data Uniq = Uniq deriving Show
data NUniq = NUniq deriving Show
data UUniq = UUniq deriving Show

并用谓词来区分它们:

type family IsMark a :: Bool
type instance IsMark Uniq = 'True
type instance IsMark NUniq = 'True
type instance IsMark UUniq = 'True

并将“转换器”从'[A, B, ..]转换为A -> B -> ..

type Id x = x
type Fn x y = x -> y

type family TL2Fun xs where
  TL2Fun '[x] = Id x
  TL2Fun (x ': xs) = Fn x (TL2Fun xs)

所以这个签名是正确的:

f1 :: TL2Fun '[Int, Int, Int]
f1 a b = a + b

现在我想要这个:

f2 :: TL2Fun (WOMarks '[Int, Int, Int, Uniq])
f2 a b = a + b

即首先从此列表中滤除Uniq。因此,我根据谓词添加了过滤器功能:

type family WOMarks xs where
  WOMarks '[] = '[]
  WOMarks (x ': xs) = If (IsMark x) (WOMarks xs) (x ': (WOMarks xs))

其中If是从Data.Type.Bool导入的(或者我可以使用PolyKind来实现)...

但是f2未编译,出现类似错误

 • Couldn't match type ‘TL2Fun
                          (If
                             (IsMark Int)
                             (If
                                (IsMark Int)
                                (If (IsMark Int) '[] '[Int])
                                (Int : If (IsMark Int) '[] '[Int]))
                             (Int
                                : If
                                    (IsMark Int)
                                    (If (IsMark Int) '[] '[Int])
                                    (Int : If (IsMark Int) '[] '[Int])))’
                  with ‘a0 -> a0 -> a0’
   Expected type: TL2Fun (WOMarks '[Int, Int, Int, Uniq])
     Actual type: a0 -> a0 -> a0
   The type variable ‘a0’ is ambiguous
 • The equation(s) for ‘f2’ have two arguments,
   but its type ‘TL2Fun (WOMarks '[Int, Int, Int, Uniq])’ has none (intero)
我的过滤尝试中似乎存在If

:它是“调用”树,但尚未对过滤结果进行“评估”(或其他原因,我不确定错误原因)。如何实现我的目标,即通过谓词删除类型?

1 个答案:

答案 0 :(得分:3)

@ M.Aroosi指出,\1,\2,\3,\4,\5,\6,\7 \8 需要一个IsMark的实例:

Int

,然后程序将键入check。当然,您不需要为每种非标记类型都定义一个实例,并使用:

type instance IsMark Int = 'False

将导致实例冲突。

解决方案是改为定义封闭类型族:

type instance IsMark a = 'False

在此之后一切正常:

type family IsMark a :: Bool where
  IsMark Uniq = 'True
  IsMark NUniq = 'True
  IsMark UUniq = 'True
  IsMark a = 'False