【发布时间】:2018-10-13 20:21:42
【问题描述】:
在“使用 Idris 进行类型驱动开发”一书中,有几个使用 monadic 数据类型对“程序”进行编码的示例(导致使用索引数据类型进行有用的类型安全编码)。通常,每个这样的数据类型都可以在特定的上下文中运行(出于学习目的,它主要是IO)。
我试图编写另一个'runner',它不会在单子上下文中执行,而是有一个函数来执行step给定一些输入 - 如果输入与当前程序状态匹配,我们会输入它的值并继续,获得下一个程序状态。
如果数据类型没有被索引,这很容易输入:
data Prog : Type -> Type where
Req : Prog String
Pure : a -> Prog a
(>>=) : Prog a -> (a -> Prog b) -> Prog b
data Event = Resp String
run : Prog a -> Event -> Prog a
run Req (Resp s) = Pure s
run (Pure x) _ = Pure x
run (x >>= f) ev = let x' = run x ev
in case x' of
Pure v => f v
v => v >>= f
还有一个样本:
prog : Prog String
prog = do
x <- Req
y <- Req
Pure (x ++ y)
test : IO ()
test = do
-- this might doesn't look reasonable , but the point is that we could
-- keep/pass program state around and execute it in whatever manner we wish
-- in some environment
let s1 = run prog (Resp "Hello, ")
let s2 = run s1 (Resp "world")
case s2 of
Pure s => putStrLn s
_ => putStrLn "Something wrong"
这一切似乎都运行良好,但是当主要数据类型被索引并以独立类型的方式(取决于结果)跟踪其状态时,事情变得复杂了:
data State = StateA | StateB
data Event = Resp String
data Indexed : (ty : Type) -> State -> (ty -> State) -> Type where
Req : Indexed String StateA (\res => case res of "frob" => StateB; _ => StateA)
Pure : (res : a) -> Indexed a (state_fn res) state_fn
(>>=) : Indexed a state1 state2_fn
-> ((res : a) -> Indexed b (state2_fn res) state3_fn)
-> Indexed b state1 state3_fn
突然间,输入run函数并不容易:
run : Indexed a s1 s2_fn -> Event -> Indexed a s3 s4_fn
不会削减它,因为调用者不会指定结果状态。这让我尝试用依赖对“隐藏”这些参数:
run : Indexed a s1 s2_fn -> Event -> (s3 ** s4_fn ** Indexed a s3 s4_fn)
意思是“为我在特定状态下运行这个程序,我不在乎结果状态(索引)会是什么”。
但是,Pure 出现问题了:
run (Pure x) _ = (?noIdea ** ?noIdeaAsWell ** (Pure x))
所以也许我们还需要输入索引:
run : (s1 ** s2_fn ** Indexed a s1 s2_fn) -> Event -> (s3 ** s4_fn ** Indexed a s3 s4_fn)
但是类型错误很快就变得太多了,很多工作只是为知道转换的依赖对“重新创建”值(无论如何,转换已经定义了)。这导致我认为我走错了路。如何尝试编写这样的解释器?
【问题讨论】:
-
找到了一种更好的方式来表达一种存在类型,它真正意味着“我不在乎”(与依赖对不同):gist.github.com/adituv/dcea611d75722560a3af64f5ae651804。只需要找出如何将其应用于“隐藏”多个类型变量。
-
我只是想知道这种解释计算的“步骤”方式是否有意义,也许更容易实现在某个 monad 的上下文中工作的传统“完整运行”函数将允许我实现并满足它将在其中运行的任何需求系统(从哪里获取输入,如何管理异步等等)。即使这意味着那里的解释器必须允许存储中间状态等,它也可以在更简单的非依赖类型上工作......
标签: idris