是否可以扩展免费的 monad 解释器?

2024-02-01

给定一个免费的 monad DSL,例如:

data FooF x = Foo String x
            | Bar Int    x
  deriving (Functor)

type Foo = Free FooF

和一个随机解释器Foo:

printFoo :: Foo -> IO ()
printFoo (Free (Foo s n)) = print s >> printFoo n
printFoo (Free (Bar i n)) = print i >> printFoo n

在我看来,应该可以在 printFoo 的每次迭代中散布一些东西,而无需手动执行:

printFoo' :: Foo -> IO ()
printFoo' (Free (Foo s n)) = print s >> print "extra info" >> printFoo' n
printFoo' (Free (Bar i n)) = print i >> print "extra info" >> printFoo' n

这是否可以通过“包装”原件来实现printFoo?


动机:我正在编写一个小型 DSL,它可以“编译”为二进制格式。二进制格式在每个用户命令之后包含一些额外信息。它必须在那里,但与我的用例完全无关。


其他答案忽略了多么简单free做这个! :) 目前你有

{-# LANGUAGE DeriveFunctor #-}

import Control.Monad.Free

data FooF x = Foo String x
            | Bar Int    x
  deriving (Functor)

type Foo = Free FooF

program :: Free FooF ()
program = do
  liftF (Foo "Hello" ())
  liftF (Bar 1 ())
  liftF (Foo "Bye" ())

printFoo :: Foo () -> IO ()
printFoo (Free (Foo s n)) = print s >> printFoo n
printFoo (Free (Bar i n)) = print i >> printFoo n
printFoo (Pure a) = return a

这使

*Main> printFoo program 
"Hello"
1
"Bye"

那很好,但是iterM可以为您做必要的管道工作

printFooF :: FooF (IO a) -> IO a
printFooF (Foo s x) = print s >> x
printFooF (Bar i x) = print i >> x

printFooBetter :: Foo () -> IO ()
printFooBetter = iterM printFooF

然后我们得到

*Main> printFooBetter program
"Hello"
1
"Bye"

好吧,太好了,和以前一样。但printFooF给我们更多 灵活地按照您想要的方式增强翻译器

printFooFExtra :: FooF (IO a) -> IO a
printFooFExtra = (print "stuff before IO action" >>)
                 . printFooF
                 . fmap (print "stuff after IO action" >>)

printFooExtra :: Foo () -> IO ()
printFooExtra = iterM printFooFExtra

然后我们得到

*Main> printFooExtra program
"stuff before IO action"
"Hello"
"stuff after IO action"
"stuff before IO action"
1
"stuff after IO action"
"stuff before IO action"
"Bye"
"stuff after IO action"

感谢 Gabriel Gonzalez 推广自由单子和 Edward Kmett 用于编写库! :)

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

是否可以扩展免费的 monad 解释器? 的相关文章

随机推荐

  • 十六进制增量/循环直到 FFF

    我有一个包含十六进制数字的字符串 我想增加该十六进制数字 直到达到最大数字 FFF 我如何循环才能获得起始十六进制和 FFF 之间的每个数字 我尝试将字符串转换为字节数组 但之后陷入困境 string stringHex 7A string
  • 使用 BigQuery 获取 Firebase Analytics 历史数据

    我已将 firebase 分析应用程序链接到 BigQuery 并在 app events 和 app events intraday 表下获取原始数据 使用 BigQuery 的主要要求是获取在 Firebase 分析仪表板下获得的分析数
  • 如何将Gitlab项目复制到另一个Gitlab存储库?

    我想将 GitLab 项目复制到另一个存储库 该存储库应该是完全独立来自原始项目 为此 我尝试将原来的项目fork到另一个项目中 但在原始项目中 维护者仍然可以看到分叉列表 并知道其他分叉的维护位置 我想要一个完整的副本 没有任何到主项目的
  • WinSCP .NET 程序集拒绝 RSA/DSA 密钥指纹

    我正在尝试使用 WinSCP NET 程序集连接到 WinSCP 服务器 我遇到的问题是它会轰炸检查主机密钥指纹 我已经创建了 RSA 密钥 我的代码如下 var server new WinSCP SessionOptions serve
  • 将某些指标与 Google Analytics API v4 中的会话和产品相关联

    我在 GA api 中需要获取一些非常具体类型的指标 站点级别 使用购物车进行的站点访问添加 这是会话次数的计数 发生在会话中发生购物车添加的时间范围内 产品视图的站点访问 这是对在会话中出现产品详细信息视图的时间范围内发生的会话数量的计数
  • 材质按钮在预览中未正确显示

    这个问题与我的另一个问题相关 材质按钮 样式应用不正确 丑陋极了 https stackoverflow com questions 53224903 material button styles not being applied cor
  • TFS 2010:当我可以使用 XamlReader 进行反序列化时,为什么无法使用 XamlWriter.Save 反序列化 Dictionary

    public static string GetXml Dictionary
  • Python ctypes 可以在 x86-64 上加载 32 位 C 库吗?

    我有一台安装了 32 位库的 64 位 RHEL 主机 一个供应商有 32 位 所以我想使用 ctypes 加载到 Python 中 from ctypes import CDLL CDLL 32bitdinosaur so OSError
  • 运行应用程序期间的 Grails 警告/错误

    目前 当我尝试在 Eclipse 中运行我的 Google App Engine Grails 测试应用程序时 我看到了以下警告 警告 目标导致名称覆盖 startLogging 警告 找不到 C Users Some Person gra
  • 如何知道子列表中某个元素的索引

    如何知道子列表中元素的索引 类似的问题在这里被问到 https stackoverflow com questions 176918 finding the index of an item given a list containing
  • UIAutomator Facebook 登录

    我为我的应用程序创建了一个 UIAutomator 登录测试 它适用于某些模拟器 问题是 它并不适用于所有人 UiObject input mDevice findObject new UiSelector instance 0 class
  • SPARQL 查询根据语句的顺序返回不同的结果

    我有一个 SPARQL 查询 它返回两个资源的最具体的常见类 当我尝试运行它时https dbpedia org sparql https dbpedia org sparql 有时它什么也不返回 有时它返回我想要的类 我注意到它与查询中语
  • Spring MVC + Hibernate:数据验证策略

    我们都知道 Spring MVC 通常与 Hibernate Validator 和 JSR 303 集成得很好 但正如有人所说 Hibernate Validator 只是用于 Bean Validation 的东西 这意味着更复杂的验证
  • ES6 类执行多态性的能力

    我试图通过 ES6 类来模拟多态性 以便能够更好地理解这个理论 概念很清楚 设计对象以共享行为并能够用特定行为覆盖共享行为 但恐怕我上面的代码不是一个有效的多态性示例 由于我缺乏经验 如果您能全面地回答以下问题 我将不胜感激 事实上 两个类
  • 使用 swift4 在 xcode 中对单个项目使用不同的 GoogleService-Info.plist

    我有一个项目 但有 4 个不同的环境 开发 暂存 质量检查 生产 我已经从移动设备的设置中给出了他们的 环境的网络服务 URL 路径 现在我想为所有这些不同的环境使用不同的 GoogleService info plist 就像当我从后端选
  • MEF 对象生命周期

    我有一个名为 Foo 的类 using System using System ComponentModel Composition namespace MefTest Export internal class Foo public Fo
  • 拟合部分高斯

    我正在尝试使用拟合高斯总和scikit学习 http scikit learn org stable index html因为 scikit learn高斯混合 http scikit learn org stable modules ge
  • Strope 在页面卸载时发送双重请求

    我用 jQuery Strope 编写了一个 XMPP 客户端 一切都运行良好 一对一 存在 MUC 等 并且包含在 jQuery 插件中 但是 当页面卸载时 它会发送 2 个具有相同 Rid 的最终请求 最近当我开始处理会话附件时 这才成
  • 如何从 GPUImageView 获取 UIImage

    我在我的项目中使用 GPUImageView 库 它使用 GPUImageView 并且我在 GPUImageView 上添加了对比度等滤镜和其他图像效果 如棕褐色等 一切都很好 但问题是现在我想将 GPUImageVIew 转换为 UII
  • 是否可以扩展免费的 monad 解释器?

    给定一个免费的 monad DSL 例如 data FooF x Foo String x Bar Int x deriving Functor type Foo Free FooF 和一个随机解释器Foo printFoo Foo gt