繁体   English   中英

操作真的与一个免费的monad同构吗?

[英]Is operational really isomorphic to a free monad?

证明

这篇博文中,Tekmo指出我们可以证明ExitSuccess退出是因为(我认为)它就像构造函数的Const函子(它不带x因此fmap行为类似于const )。

通过操作包,Tekmo的TeletypeF可能会被翻译成如下:

data TeletypeI a where
    PutStrLn :: String -> TeletypeI ()
    GetLine :: TeletypeI String
    ExitSuccess :: TeletypeI ()

我已经读过,操作与一个免费monad是同构的,但我们可以在这里证明ExitSuccess退出吗? 在我看来它遇到了与exitSuccess :: IO ()完全相同的问题,特别是如果我们要为它编写一个解释器,我们需要把它写成就像它没有退出一样:

eval (ExitSuccess :>>= _) = exitSuccess

与不涉及任何模式通配符的免费monad版本相比:

run (Free ExitSuccess) = exitSuccess

怠惰

操作Monad教程中, apfelmus提到了一个缺点:

状态monad表示为s - >(a,s)可以应对一些无限的程序,如

 evalState (sequence . repeat . state $ \\s -> (s,s+1)) 0 

而指令列表方法没有希望处理它,因为只有最后一个Return指令才能返回值。

对于免费的monad也是如此吗?

(请允许我通过大胆结合以前的答案来获得大奖。;-))

关键的观察是:证明究竟是什么? 根据Free TeletypeF的表述,我们可以证明以下内容:

Free TeletypeF a类型程序的每个解释器在遇到ExitSuccess指令时必须退出。 换句话说,我们自动获得代数定律

  interpret (exitSuccess >>= k) = interpret exitSuccess 

因此, Free monad实际上允许您将某些代数定律烘焙到该类型中。

相反,操作方法不限制ExitSuccess的语义,没有与每个解释器相关的相关代数法。 可以编写在遇到此指令时退出的解释器,但也可以编写不能解释的解释器。

当然,您可以通过检查证明任何特定的解释器满足法律,例如因为它使用通配符模式匹配。 Sjoerd Visscher观察到您还可以通过将ExitSuccess的返回类型ExitSuccessVoid来在类型系统中强制执行此操作。 然而,这不适用于可以融入自由单子的其他法律,例如mplus指令的分配法则。

因此,在一个令人困惑的事件转变中,如果你将“自由”解释为“最少量的代数定律”,那么操作方法比免费monad更自由。

这也意味着这些数据类型不是同构的。 但是,它们是等价的:每个用Free编写的解释器都可以转换成用Program编写的解释器,反之亦然。

就个人而言,我喜欢将我的所有法律都放入翻译中,因为有许多法律无法融入免费的monad中,我喜欢将它们全部放在一个地方。

答案是肯定的,但仅当您使用TeletypeF的其他翻译时:

data TeletypeI a where
    PutStrLn :: String -> TeletypeI ()
    GetLine :: TeletypeI String
    ExitSuccess :: TeletypeI Void

TeletypeI的论点是操作将/必须提供给程序的其余部分。 它是继续k in的参数的类型

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 VoidIO a来修复。

答案是否定的,你无法证明操作者忽略了exitSuccess上的其余程序。 TeletypeITeletypeF对比以了解原因。 为了便于比较,我将用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终止。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM