这是我要实现的目标:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
module Action where
import Data.Type.Set
data Configuration
= A
| B
| C
data Action (configuration :: Configuration) where
Action1 :: Member cfg '[ 'A ] => Action cfg
Action2 :: Member cfg '[ 'B, 'C ] => Action cfg
Action3 :: Member cfg '[ 'A, 'C ] => Action cfg
exhaustive :: Action 'A -> ()
exhaustive Action1 = ()
exhaustive Action3 = ()
我有一组操作和一组配置,但是某些操作仅在某些配置中才有意义。我不想不必显式丢弃配置中不相关的操作,因此我考虑使用GADT。不幸的是,类型检查器无法意识到我的exhaustive
函数确实是详尽无遗的。
我想知道我是否可以使用任何现有的类型级别列表/集合,甚至可以使用行类型(如http://hackage.haskell.org/package/row-types-0.2.3.0/docs/Data-Row-Variants.html)来解决此问题。
我还尝试了一种方法Action2 :: Action '[ 'B, 'C ]
并将类型类约束推入exhaustive
,但没有成功。
谢谢您的任何建议! (或者甚至是为什么这是一个坏主意或不容易实现的原因)
答案 0 :(得分:3)
一个朋友建议一个解决方案:
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
module Action where
type family MemberB (x :: k) (l :: [k]) where
MemberB x '[] = 'False
MemberB x (x:xs) = 'True
MemberB x (x':xs) = MemberB x xs
type Member x xs = MemberB x xs ~ 'True
data Configuration
= A
| B
| C
data Action (configuration :: Configuration) where
Action1 :: Member cfg '[ 'A ] => Action cfg
Action2 :: Member cfg '[ 'B, 'C ] => Action cfg
Action3 :: Member cfg '[ 'A, 'C ] => Action cfg
exhaustive :: Action 'A -> ()
exhaustive Action1 = ()
exhaustive Action3 = ()
exhaustive Action2 = ()
显然,问题在于我使用的Member
(来自Data.Type.Set
)不是封闭类型的族。现在,错误消息不是很好,我们尝试执行以下操作:
type family MemberB (x :: k) (l :: [k]) where
MemberB x '[] = TypeError ('Text "not a member")
MemberB x (x:xs) = 'True
MemberB x (x':xs) = MemberB x xs
但是不幸的是,这吃了类型错误!是因为TypeError
将与'True
统一在一起吗?如果有人有办法使类型错误稍微好一点,我会很高兴地接受它!
我对此提出了一个单独的问题:
How to define a custom type error within a type family for a constraint that uses type equality?