使用幻像类型时避免冗余约束
Avoiding redundant constraints when working with phantom types
这是我正在尝试编写的一个简化的、可能很愚蠢的示例(它更复杂并且涉及列表长度的编译时编码)。
鉴于以下情况:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE GADTs #-}
data D (a :: Bool) where
D :: Bool -> D a
我想要以下功能g
:
g :: D a -> Bool
g (D x) = x == a
当然这不会编译,因为 a
是一个类型,而不是一个值。
这是一个可能的解决方案:
class C (a :: Bool) where
f :: D a -> Bool
instance C True where
f (D x) = x == True
instance C False where
f (D x) = x == False
g :: (C a) => D a -> Bool
g = f
但是我必须向 g
添加一个约束,这似乎是多余的 a :: Bool
,而且我已经为 Bool
的所有情况提供了实例。
有没有我可以写 g
的签名:
g :: D a -> Bool
即不需要额外的约束?
不,这是不可能的,因为我可以给你一个完美的 D Any
类型的值,其中定义了 Any
type family Any :: k where {}
你能做的就是写一个更普遍有用的class:
data SBool a where
SFalse :: SBool 'False
STrue :: SBool 'True
sBoolToBool :: SBool a -> Bool
sBoolToBool SFalse = False
sBoolToBool STrue = True
class KnownBool a where
knownBool :: SBool a
instance KnownBool 'False where
knownBool = SFalse
instance KnownBool 'True where
knownBool = STrue
当然,如果您不将这些类型用于其他任何用途,那么所有这些机制就真的有点过分了。
这是我正在尝试编写的一个简化的、可能很愚蠢的示例(它更复杂并且涉及列表长度的编译时编码)。
鉴于以下情况:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE GADTs #-}
data D (a :: Bool) where
D :: Bool -> D a
我想要以下功能g
:
g :: D a -> Bool
g (D x) = x == a
当然这不会编译,因为 a
是一个类型,而不是一个值。
这是一个可能的解决方案:
class C (a :: Bool) where
f :: D a -> Bool
instance C True where
f (D x) = x == True
instance C False where
f (D x) = x == False
g :: (C a) => D a -> Bool
g = f
但是我必须向 g
添加一个约束,这似乎是多余的 a :: Bool
,而且我已经为 Bool
的所有情况提供了实例。
有没有我可以写 g
的签名:
g :: D a -> Bool
即不需要额外的约束?
不,这是不可能的,因为我可以给你一个完美的 D Any
类型的值,其中定义了 Any
type family Any :: k where {}
你能做的就是写一个更普遍有用的class:
data SBool a where
SFalse :: SBool 'False
STrue :: SBool 'True
sBoolToBool :: SBool a -> Bool
sBoolToBool SFalse = False
sBoolToBool STrue = True
class KnownBool a where
knownBool :: SBool a
instance KnownBool 'False where
knownBool = SFalse
instance KnownBool 'True where
knownBool = STrue
当然,如果您不将这些类型用于其他任何用途,那么所有这些机制就真的有点过分了。