如何使用免费 Monad 的 Church 编码?

2024-04-20

我一直在使用Free数据类型在Control.Monad.Free来自free包裹。现在我正在尝试将其转换为使用F in Control.Monad.Free.Church但不知道如何映射功能。

例如,一个简单的模式匹配函数使用Free看起来像这样 -

-- Pattern match Free
matchFree
  :: (a -> r)
  -> (f (Free f a) -> r)
  -> Free f a
  -> r
matchFree kp _ (Pure a) = kp a
matchFree _ kf (Free f) = kf f

我可以轻松地将其转换为使用的函数F通过转换为/从Free -

-- Pattern match F (using toF and fromF)
matchF
  :: Functor f
  => (a -> r)
  -> (f (F f a) -> r)
  -> F f a
  -> r
matchF kp kf = matchF' . fromF
  where
    matchF' (Pure a) = kp a
    matchF' (Free f) = kf (fmap toF f)

但是我不知道如何在不使用的情况下完成它toF and fromF -

-- Pattern match F (without using toF)???
-- Doesn't compile
matchF
  :: Functor f
  => (a -> r)
  -> (f (F f a) -> r)
  -> F f a
  -> r
matchF kp kf f = f kp kf

一定有一个我缺少的一般模式。你能帮我弄清楚吗?


您询问“您缺少的一般模式”。让我尝试解释一下,尽管 Petr Pudlák 的回答也相当不错。正如 user3237465 所说,我们可以使用两种编码,Church 和 Scott,并且您使用的是 Scott 而不是 Church。这是一般评论。

编码如何工作

通过连续传递,我们可以描述任何类型的值x通过类型的某些独特功能

data Identity x = Id { runId :: x } 
{- ~ - equivalent to - ~ -} 
newtype IdentityFn x = IdFn { runIdFn ::  forall z. (x -> z) -> z }

这里的“forall”非常重要,它表示这种类型离开z作为未指定的参数。双射是Id . ($ id) . runIdFn来自IdentityFn to Identity while IdFn . flip ($) . runId反之亦然。之所以出现这种等价,是因为人们基本上无法对该类型做任何事情forall z. z,没有足够普遍的操作。我们可以等价地指出newtype UnitFn = UnitFn { runUnitFn :: forall z. z -> z }只有一个元素,即UnitFn id,这意味着它对应于单位类型data Unit = Unit以类似的方式。

现在柯里化观察(x, y) -> z同构于x -> y -> z是连续传递的冰山一角,它允许我们用纯函数来表示数据结构,没有数据结构,因为显然类型Identity (x, y)因此相当于forall z. (x -> y -> z) -> z。因此,将两个项目“粘合”在一起与创建这种类型的值相同,只是使用纯函数作为“粘合”。

要看到这种等价性,我们只需处理其他两个属性。

第一个是 sum 类型构造函数,其形式为Either x y -> z. See, Either x y -> z同构于

newtype EitherFn x y = EitherFn { runEitherFn :: forall z. (x -> z) -> (y -> z) -> z }

从中我们得到了该模式的基本思想:

  1. 采用新的类型变量z没有出现在表达式正文中。
  2. 对于数据类型的每个构造函数,创建一个函数类型,该函数类型将其所有类型参数作为参数,并返回z。将这些“处理程序”称为与构造函数相对应的。所以处理程序(x, y) is (x, y) -> z我们咖喱到x -> y -> z,以及处理程序Left x | Right y are x -> z and y -> z。如果没有参数的话,直接取一个值即可z作为你的功能而不是更麻烦的() -> z.
  3. 将所有这些处理程序作为表达式的参数forall z. Handler1 -> Handler2 -> ... -> HandlerN -> z.
  4. 同构的一半基本上只是将构造函数作为所需的处理程序传递;构造函数上的其他模式匹配并应用相应的处理程序。

微妙的缺失的东西

同样,将这些规则应用于各种事物是很有趣的;例如,正如我上面提到的,如果您将其应用于data Unit = Unit你发现任何单位类型都是恒等函数forall z. z -> z,如果你将其应用到data Bool = False | True你找到逻辑函数forall z. z -> z -> z where false = const while true = const id。但如果你真的使用它,你会发现仍然缺少一些东西。提示:如果我们看一下

data List x = Nil | Cons x (List x)

我们看到该模式应该如下所示:

data ListFn x = ListFn { runListFn :: forall z. z -> (x -> ??? -> z) -> z }

对于一些???。上述规则并没有规定那里会发生什么。

有两个不错的选择:要么我们使用newtype尽其所能地ListFn x那里(“Scott”编码),或者我们可以用我们已经给出的函数预先减少它,在这种情况下它就变成了z使用我们已有的功能(“Church”编码)。现在,由于我们已经预先执行了递归,因此 Church 编码仅与以下内容完全等效finite数据结构; Scott 编码可以处理无限列表等。也很难理解如何以 Church 形式编码相互递归,而 Scott 形式通常更简单一些。

不管怎样,教会编码有点难以思考,但也更神奇,因为我们可以带着一厢情愿的想法来接近它:“假设这个z已经是你想要实现的目标tail list,然后将其与head list以适当的方式。”而这种一厢情愿的想法正是人们难以理解的原因foldr,因为该双射的一侧恰好是foldr列表中的。

还有一些其他问题,例如“如果,例如Int or Integer,构造函数的数量是大还是无限?”。这个特定问题的答案是使用函数

data IntFn = IntFn { runIntFn :: forall z. (z -> z) -> z -> z }

你问这是什么?好吧,一个聪明的人(Church)已经发现这是一种将整数表示为组合重复的方法:

zero f x = x
one f x = f x
two f x = f (f x)
{- ~ - increment an `n` to `n + 1` - ~ -}
succ n f = f . n f

其实在这个账户上m . n是两者的产物。但我提到这一点是因为插入一个并不太难()并翻转争论发现这实际上是forall z. z -> (() -> z -> z) -> z这是列表类型[()],其值由下式给出length和添加由++和乘法给出>>.

为了提高效率,您可以进行 Church-encodedata PosNeg x = Neg x | Zero | Pos x并使用 Church 编码(保持有限!)[Bool]形成教会编码PosNeg [Bool]其中每个[Bool]隐含地以未声明的方式结束True在最后的最高有效位,因此[Bool]代表从+1到无穷大的数字。

扩展示例:BinLeaf / BL

另一个重要的例子,我们可能会想到二叉树,它将所有信息存储在叶子中,但也包含内部节点上的注释:data BinLeaf a x = Leaf x | Bin a (BinLeaf a x) (BinLeaf a x)。按照 Church 编码的方法,我们这样做:

newtype BL a x = BL { runBL :: forall z. (x -> z) -> (a -> z -> z -> z) -> z}

现在代替Bin "Hello" (Leaf 3) (Bin "What's up?" (Leaf 4) (Leaf 5)我们用小写字母构造实例:

BL $ \leaf bin -> bin "Hello" (leaf 3) (bin "What's up?" (leaf 4) (leaf 5)

因此,同构是非常简单的一种方式:binleafFromBL f = runBL f Leaf Bin。对方有案件派遣,但还不算太糟糕。

递归数据上的递归算法怎么样?这就是它变得神奇的地方:foldr and runBL在我们到达树本身之前,Church 编码都已经运行了子树上的任何函数。假设我们想要模拟这个函数:

sumAnnotate :: (Num n) => BinLeaf a n -> BinLeaf (n, a) n
sumAnnotate (Leaf n) = Leaf n
sumAnnotate (Bin a x y) = Bin (getn x' + getn y', a) x' y' 
    where x' = sumAnnotate x
          y' = sumAnnotate y
          getn (Leaf n) = n
          getn (Bin (n, _) _ _) = n

我们该做什么?

-- pseudo-constructors for BL a x.
makeLeaf :: x -> BL a x
makeLeaf x = BL $ \leaf _ -> leaf x

makeBin :: a -> BL a x -> BL a x -> BL a x
makeBin a l r = BL $ \leaf bin -> bin a (runBL l leaf bin) (runBL r leaf bin)

-- actual function
sumAnnotate' :: (Num n) => BL a n -> BL n n
sumAnnotate' f = runBL f makeLeaf (\a x y -> makeBin (getn x + getn y, a) x y) where
    getn t = runBL t id (\n _ _ -> n)

我们传入一个函数\a x y -> ... :: (Num n) => a -> BL (n, a) n -> BL (n, a) n -> BL (n, a) n。请注意,这两个“参数”与此处的“输出”类型相同。使用教会编码,我们必须像已经成功一样进行编程——一门叫做“一厢情愿”的学科。

自由单子的教会编码

自由单子有范式

data Free f x = Pure x | Roll f (Free f x)

我们的教会编码程序说这变成了:

newtype Fr f x = Fr {runFr :: forall z. (x -> z) -> (f z -> z) -> z}

你的职能

matchFree p _ (Pure x) = p x
matchFree _ f (Free x) = f x

变得简单

matchFree' p f fr = runFr fr p f
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使用免费 Monad 的 Church 编码? 的相关文章

随机推荐

  • 模块化 AngularJS 应用程序:一个或多个 AngularJS 模块?

    我尝试使用 AngularJS 构建一个模块化应用程序 我的第一个想法是使用这种文件夹结构按功能对每个模块进行分组 core controllers js directives js app js modules users control
  • C++ 错误 C2040?

    错误信息 这是什么意思 我该如何解决它 错误 C2040 int 与 const char 2 的间接级别不同 Code include
  • 找不到方法 commandLine()

    我正在尝试将预构建 shell 脚本添加到我的 gradle Android Studio 构建中 我已添加以下内容app build gradle task prePreBuild lt lt commandLine ls preBuil
  • 根据列条件连接数据框行

    为了后续的讨论 我将参考下面的示例数据框 现在 我希望实现的是将所有相似的数据包时间分组 即所有 7s 12s 等 此外 PacketTime字段应包含最小值和最大值的差异 max PacketTime min PacketTime 以及F
  • Fortran90 数组将空白值读取为 null

    我正在读取外部文本文件的数据 30 行 7 列 每行用 分隔 我缺少表示为 的值 当我将数据读入二维数组时 缺失值被 0 00 替换 但数据中也有 0 00 值 当我计算平均值时 计数 项目数 n 显示为计数 缺失值的数量 我如何动态选择缺
  • 调试断言失败

    我不断遇到这种情况 Debug assertions failed 当我在调试模式下运行程序时出错 我尝试在 Visual C 网站上查找此错误 但这些解释对我来说太先进了 而且它们与我对问题的最佳猜测没有任何相似之处 我已经检查了我的代码
  • 使用 Java 查找句子中的确切单词

    我正在编写一个代码来识别文本中的国家 地区名称 我正在使用一本包含国家名称的字典India America Sri Lanka 我目前正在使用text contains key with key从字典中 然而 即使对于像这样的字符串 这也会
  • 在 C 语言中,stdout 缓冲区的大小是多少?

    今天我了解到 stdout 在设置为终端时是行缓冲的 并且在不同情况下是缓冲的 因此 在正常情况下 如果我使用 printf 而不终止 n 只有当缓冲区已满时 它才会打印在屏幕上 如何获得这个缓冲区的大小 它有多大 实际大小由各个实现定义
  • “SolidBrush”参数类型对于格式化属性“Foreground”无效。参数名称:值

    我尝试在调用方法中更改颜色文本 RichTextBox wpf 但我遇到了一些麻烦 我的麻烦是 SolidBrush 参数类型对于格式化属性 Foreground 无效 参数名称 值 My code MethodInvoker action
  • 如何从 2 个数组创建地图?

    我有一个字符串数组和一个整数数组 如何使用第一个作为键 第二个作为值来创建地图 val keys arrayOf butter milk apples val values arrayOf 5 10 42 val map Map
  • 滚动“返回顶部”链接时显示/隐藏 div

    我无法让我的 转到顶部 id arrow updiv 在打开时消失 例如页面顶部 在页面顶部我得到了 所以我想要arrow up div to visible show slow 当不在页面顶部时 var tmp window height
  • Spring中如何从WebRequest获取请求的URI?

    我正在使用以下方法处理 REST 异常 ControllerAdvice and ResponseEntityExceptionHandler在 Spring Rest Web 服务中 到目前为止 一切都工作正常 直到我决定添加URI路径
  • php 的内容长度标头被覆盖!

    我试图弄清楚为什么 php 的 Content Length 标头被覆盖 这是演示 php 获取标头的请求 curl I http someserver com demo php HTTP 1 1 200 OK Date Tue 19 Ju
  • 如何快速将一个float打包为4个字节?

    我一直在寻找一种在 WebGL 纹理上存储浮动的方法 我找到了一些解决方案 http aras p info blog 2009 07 30 encoding floats to rgba the final 在互联网上 但那些只处理 0
  • 在Angular2中,使用zone.run与changeDecotor.markForCheck()的优点

    我想知道使用其中一种比另一种有什么优点或缺点 constructor private app ApplicationRef private ref ChangeDetectorRef this ref markForCheck OR thi
  • 获取列名,其中值是 pandas 数据框中的内容

    我试图在每个时间戳找到数据帧中的列名称 其值与同一时间戳的时间序列中的列名称相匹配 这是我的数据框 gt gt gt df col5 col4 col3 col2 col1 1979 01 01 00 00 00 1181 220328 9
  • 使用 Tabula 从 PDF 中提取表格

    我遇到了一个名为 Tabula 的很棒的图书馆 它几乎成功了 不幸的是 第一页上有很多无用的区域 我不希望 Tabula 提取这些区域 根据文档 您可以指定要从中提取的页面区域 但是 无用区域仅位于 PDF 文件的第一页 因此 对于所有后续
  • 使用纯 JavaScript 从另一个(php)文件获取 JSON?

    我是 JavaScript 新手 我有一个 php 文件 其中列出了目录中的所有文件 我想调用该文件并仅使用 javascript 获取它回显的 json 数组 我知道 jquery 可以做到 但这是我唯一需要做的事情 它不值得学习 jqu
  • 无法使用 XCode/Obj-C 编译 Cocoapods – “Pods-prefix.pch.dia:没有这样的文件或目录”

    我正在使用 XCode 4 5 1 和 iOS 6 0 基础 SDK 这是我收到的错误 i686 apple darwin11 llvm gcc 4 2 Users fahim Library Developer Xcode Derived
  • 如何使用免费 Monad 的 Church 编码?

    我一直在使用Free数据类型在Control Monad Free来自free包裹 现在我正在尝试将其转换为使用F in Control Monad Free Church但不知道如何映射功能 例如 一个简单的模式匹配函数使用Free看起来