因此,可以像这样定义成员资格约束:
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE GADTs #-}
module Whatever where
type family MemberB (x :: k) (l :: [k]) where
MemberB _ '[] = 'False
MemberB a (a : xs) = 'True
MemberB a (b : xs) = MemberB a 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 = ()
但是我们收到的错误消息不是很有用:
• Couldn't match type ‘'False’ with ‘'True’
Inaccessible code in
a pattern with constructor:
Action2 :: forall (cfg :: Configuration).
Member cfg '['B, 'C] =>
Action cfg,
in an equation for ‘exhaustive’
• In the pattern: Action2
In an equation for ‘exhaustive’: exhaustive Action2 = () (intero)
最好使用新的TypeError
功能来改进此消息,但是,一个幼稚的解决方案吞噬了错误:
import GHC.TypeLits
type family MemberB (x :: k) (l :: [k]) where
MemberB _ '[] = TypeError ('Text "not a member")
MemberB a (a : xs) = 'True
MemberB a (b : xs) = MemberB a xs
似乎TypeError
的行为与任何类型一样,因此它与'True
完美地统一了吗?
是否有一种在保留成员资格行为的同时获得良好的类型错误的方法?
答案 0 :(得分:4)
好吧,它不使用TypeError
,但是您可能仍然会发现它很有趣:
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE GADTs #-}
module Whatever where
data IsMember k = IsMember | Isn'tMember k [k]
type family MemberB (x :: k) (l :: [k]) (orig :: [k]) where
MemberB a '[] ys = 'Isn'tMember a ys
MemberB a (a : xs) ys = 'IsMember
MemberB a (b : xs) ys = MemberB a xs ys
type Member x xs = MemberB x xs xs ~ 'IsMember
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 = ()
该错误现在可以提供更多信息:
test.hs:32:16: error:
• Couldn't match type ‘'Isn'tMember 'A '['B, 'C]’ with ‘'IsMember’
Inaccessible code in
a pattern with constructor:
Action2 :: forall (cfg :: Configuration).
Member cfg '['B, 'C] =>
Action cfg,
in an equation for ‘exhaustive’
• In the pattern: Action2
In an equation for ‘exhaustive’: exhaustive Action2 = ()
|
32 | exhaustive Action2 = ()
| ^^^^^^^
答案 1 :(得分:1)
[XamlCompilation(XamlCompilationOptions.Compile)]
正在处理一个永远不会发生的案例,但这并不是一个错误。至少,即使现在可以改进类型系统以不允许处理不可能的情况,它仍然可以按预期的方式工作。
exhaustive
上的模式匹配向您的上下文提供约束Action2
。这不同于使用Member 'A '[ 'B, 'C ]
作为表达式,需要该约束,并且会导致约束求解器出错。
答案 2 :(得分:0)
我认为您可能想回到第一次尝试:
type family MemberB (x :: k) (l :: [k]) where
MemberB _ '[] = 'False
MemberB a (a : xs) = 'True
MemberB a (b : xs) = MemberB a xs
但是让我们修复Member
。
type Member x l = Member' x l (MemberB x l)
type family Member' x l mem :: Constraint where
Member' x l 'True = ()
Member' x l 'False =
TypeError ('ShowType x :<>:
'Text " is not a member of " :<>:
'ShowType l)