在Haskell的异类列表中键入安全查找
我想为以下数据类型开发一个类型安全的查找函数:
data Attr (xs :: [(Symbol,*)]) where
Nil :: Attr '[]
(:*) :: KnownSymbol s => (Proxy s, t) -> Attr xs -> Attr ('(s , t) ': xs)
显而易见的查找功能如下所示:
lookupAttr :: (KnownSymbol s, Lookup s env ~ 'Just t) => Proxy s -> Attr env -> t
lookupAttr s ((s',t) :* env')
= case sameSymbol s s' of
Just Refl -> t
Nothing -> lookupAttr s env'
Lookup
类型族是在单例库中定义的。 此定义无法在GHC 7.10.3上进行类型检查,并显示以下错误消息:
Could not deduce (Lookup s xs ~ 'Just t)
from the context (KnownSymbol s, Lookup s env ~ 'Just t)
bound by the type signature for
lookupAttr :: (KnownSymbol s, Lookup s env ~ 'Just t) =>
Proxy s -> Attr env -> t
该消息是为递归调用lookupAttr s env'
生成lookupAttr s env'
。 这是合理的,因为我们有如果
Lookup s ('(s',t') ': env) ~ 'Just t
持有和
s :~: s'
那么不可证明
Lookup s env ~ 'Just t
必须持有。 我的问题是,我怎么能说服Haskell类型检查器这确实是真的?
Lookup
的定义是:==
相等,它来自这里。 粗略地说, Lookup
是这样实现的:
type family Lookup (x :: k) (xs :: [(k, v)]) :: Maybe v where
Lookup x '[] = Nothing
Lookup x ('(x' , v) ': xs) = If (x :== x') (Just v) (Lookup x xs)
sameSymbol s s'
上的模式匹配不会提供任何关于Lookup s env
,也不会让GHC减少它。 我们需要知道s :== s'
,为此我们需要使用单例版本:==
。
data Attr (xs :: [(Symbol,*)]) where
Nil :: Attr '[]
(:*) :: (Sing s, t) -> Attr xs -> Attr ('(s , t) ': xs)
lookupAttr :: (Lookup s env ~ 'Just t) => Sing s -> Attr env -> t
lookupAttr s ((s', t) :* env') = case s %:== s' of
STrue -> t
SFalse -> lookupAttr s env'
一般情况下,你不应该使用KnownSymbol
, sameSymbol
,或任何东西在GHC.TypeLits
因为他们太“低级”的,不与一起玩singletons
默认。
当然,你可以编写你自己的Lookup
和其他函数,并且不需要使用singletons
导入; 重要的是你同步术语级别和类型级别,以便术语级别模式匹配产生类型级别的相关信息。
上一篇: Type safe lookup on heterogeneous lists in Haskell
下一篇: Why does my AdaptiveTrigger fire when I change the theme settings?