在this博文中,Tekmo指出我们可以证明ExitSuccess
退出是因为(我认为)它就像那个构造函数的Const
仿函数(它不带x
fmap
的行为与const
}相似。
通过操作包,Tekmo的TeletypeF
可能会被翻译成这样的内容:
data TeletypeI a where
PutStrLn :: String -> TeletypeI ()
GetLine :: TeletypeI String
ExitSuccess :: TeletypeI ()
我已经读过,操作与免费monad是同构的,但我们可以在这里证明ExitSuccess
退出吗?在我看来,它遇到了与exitSuccess :: IO ()
完全相同的问题,特别是如果我们要为它编写一个解释器,我们需要把它写成就像它没有退出一样: / p>
eval (ExitSuccess :>>= _) = exitSuccess
与不涉及任何模式通配符的免费monad版本比较:
run (Free ExitSuccess) = exitSuccess
在Operational Monad Tutorial apfelmus中提到了一个缺点:
状态monad表示为s - > (a,s)可以应付一些无限的 像
这样的节目evalState (sequence . repeat . state $ \s -> (s,s+1)) 0
而指令列表方法没有希望处理 因为只有最后一个Return指令才能返回值。
对于免费的monad也是如此吗?
答案 0 :(得分:16)
(请允许我通过大胆结合以前的答案来获得大奖。; - ))
关键的观察是:证明究竟是什么? Free TeletypeF
方面的表述允许我们证明以下内容:
类型
Free TeletypeF a
的程序的每个解释器必须在遇到ExitSuccess
指令时退出。换句话说,我们自动获得代数定律interpret (exitSuccess >>= k) = interpret exitSuccess
因此,Free
monad实际上允许你将某些代数定律加入到该类型中。
相比之下,操作方法并不限制ExitSuccess
的语义,没有与每个解释器相关的相关代数法。可以编写在遇到此指令时退出的解释器,但也可以编写不能解释的解释器。
当然,您可以通过检查证明任何特定的口译员满足法律,例如因为它使用通配符模式匹配。 Sjoerd Visscher观察到您还可以通过将ExitSuccess
的返回类型更改为Void
来在类型系统中强制执行此操作。但是,这不适用于可以融入自由单子的其他法律,例如mplus
指令的分配法。
因此,如果你将“自由”解释为“最少量的代数法则”,那么在一系列令人困惑的事件中,操作方法比免费monad更自由。
这也意味着这些数据类型不是同构的。但是,它们是等价的:用Free
编写的每个解释器都可以转换为用Program
编写的解释器,反之亦然。
就我个人而言,我喜欢将我的所有法律都放入翻译中,因为有很多法律无法被融入免费的monad中,我喜欢将它们全部放在一个地方
答案 1 :(得分:11)
答案是肯定的,但前提是您使用TeletypeF
的其他翻译:
data TeletypeI a where
PutStrLn :: String -> TeletypeI ()
GetLine :: TeletypeI String
ExitSuccess :: TeletypeI Void
TeletypeI
的参数是操作将/必须提供给程序其余部分的内容。它是
k
的参数类型
eval (ExitSuccess :>>= k) = ...
由于没有Void
类型的值,我们可以确定永远不会调用k
。 (一如既往,我们必须在这里忽略undefined
。)
等效类型是:
data TeletypeI a where
PutStrLn :: String -> TeletypeI ()
GetLine :: TeletypeI String
ExitSuccess :: TeletypeI a
现在我们必须为k
提供与任何类型匹配的值,我们也不能这样做。这可能更实用,因为singleton ExitSuccess
现在具有灵活类型Program TeletypeI a
。
同样,exitSuccess
可以通过为其提供IO Void
或IO a
类型来修复。
答案 2 :(得分:5)
答案是否定的,你无法证明操作者忽略了exitSuccess
上程序的其余部分。将TeletypeI
与TeletypeF
对比以了解原因。为了便于比较,我将在GADT表示法中重写TeletypeF
data TeletypeF x where | data TeletypeI x where
PutStrLn :: String -> x -> TeletypeF x | PutStrLn :: String -> TeletypeI ()
GetLine :: (String -> x) -> TeletypeF x | GetLine :: TeletypeI String
ExitSuccess :: TeletypeF x | ExitSuccess :: TeletypeI ()
使用TeletypeF
,我们可以立即构建实际程序:
GetLine (\str -> PutStrLn (map toUpper str) ExitSuccess)
TeletypeI
没有像TeletypeF
那样的方式来引用“程序的其余部分”。
-- TeletypeF:
GetLine (\str -> "rest of program" goes here)
-- or
PutStrLn someString ("rest of program" goes here)
-- or
ExitSuccess -- there is no "rest of program" slot provided
由于TeletypeI
缺少此“其他程序”信息,因此当您遇到ExitSuccess
时,您将无法获得任何知识。
-- TeletypeI
PutStrLn someString -- no information about "rest of program"
-- or
GetLine -- no information about "rest of program"
-- or
ExitSuccess -- no information about "rest of program"
允许来的是“程序的其余部分”完全取决于Program
类型,它对应用它的指令集一无所知。它只允许您将指令绑定在一起,并通过Return
终止。