Итак, можно определить ограничение членства следующим образом:
{-# 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
?
Есть ли способ получить приятную ошибку типа при сохранении поведения членства?