我试图在Haskell中使用依赖类型编程获得一些乐趣,更具体地说是使用类型安全查找操作。 Previously,我已经询问了如何为以下数据类型实现查找操作:
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'
工作正常。现在,我想定义一个类型语法值的异构列表,由类型Exp
表示:
{-# LANGUAGE GADTs, TypeFamilies, DataKinds, TypeOperators, PolyKinds #-}
import Data.Singletons.Prelude.List
import Data.Singletons.Prelude.Bool
import Data.Singletons.Prelude.Eq
data Exp (env :: [(Symbol,*)]) :: * -> * where
Value :: String -> Exp env String
Var :: (Lookup s env ~ 'Just t) => Sing s -> Exp env t
Op :: Exp env t -> Exp env t -> Exp env t
异构列表类型由Env
数据类型定义:
data Env (env :: [(Symbol,*)]) where
Nil :: Env '[]
Cons :: (Lookup s' env ~ 'Nothing) =>
(Sing s' ,
Exp ('(s', a) ': env) a) ->
Env env ->
Env ('( s',a) ': env)
使用这些数据类型,我使用某种存在量化(类型Ex2
)定义查找函数:
data Ex2 (p :: k -> k' -> *) where
Ex2 :: p e i -> Ex2 p
lookupEnv :: Lookup s env ~ 'Just t => Sing s -> Env env -> Ex2 Exp
lookupEnv s (Cons (s',e) env)
= case s %:== s' of
STrue -> Ex2 e
SFalse -> lookupEnv s env
到目前为止,这么好。现在,我遇到了一些有趣的问题:
1)有没有办法定义lookupEnv
而不使用Ex2
类型提供的这种存在量化?
2)假设我想定义一个操作来修改类型为Env
的值的给定条目。明确尝试定义此函数是
modifyEnv :: Lookup s env ~ 'Just t => Sing s -> Exp env t -> Env env -> Env env
modifyEnv s e (Cons (s',e') env')
= case s %:== s' of
STrue -> Cons (s', Op e e') env'
SFalse -> Cons (s',e') (modifyEnv s e env')
函数modifyEnv
在环境中与另一个表达式组合。
GHC不接受此功能,返回隐藏的错误消息。我怎么能定义这样的修改函数?
答案 0 :(得分:2)
我意识到这不是一个完整的解决方案,因为它没有使用Symbol
- 索引Env
讽刺,但它可能会给你一个很好的起点。我正在使用“类型de Bruijn索引到上下文”类型:
{-# LANGUAGE GADTs, TypeFamilies, DataKinds, TypeOperators #-}
data Var (ctx :: [*]) :: * -> * where
Here :: Var (a ': ctx) a
There :: Var ctx a -> Var (b ': ctx) a
data Exp (ctx :: [*]) :: * -> * where
Value :: String -> Exp ctx String
Var :: Var ctx t -> Exp ctx t
Op :: Exp ctx t -> Exp ctx t -> Exp ctx t
data Env (ctx :: [*]) where
Nil :: Env '[]
Cons :: Exp (a ': ctx) a -> Env ctx -> Env (a ': ctx)
这允许编写lookupEnv
的打字版本,如下所示:
lookupEnv :: Env ctx -> Var ctx t -> Exp ctx t
lookupEnv (Cons e env) v = case v of
Here -> e
There v -> weaken $ lookupEnv env v
通过编写以下弱化函数:
weaken :: Exp ctx t -> Exp (a ': ctx) t
weaken (Value s) = Value s
weaken (Var v) = Var (There v)
weaken (Op f e) = Op (weaken f) (weaken e)
在我看来,使用Symbol
- 索引的上下文,这是最后一个weaken
步骤失败,因为Lookup s ctx ~ Nothing
似乎没有足够的GHC结构证明你需要weaken
(你可以改变上下文,因为你知道不会出现阴影问题)。