使用Polysemy编写Discord库

最近,我在阅读了尽可能多的博客文章后,将我的库从mtl / transformers 迁移到了 polysemy。我想迁移的主要原因是,每次我在堆栈中有多个效果时都不必编写新类型和所有N个实例,以及编写新效果需要多少样板多义性,从而避免了迁移。

在这篇以及即将发表的博客文章中,我将讲述在进行转换时我所面临和解决的挑战。

记录中

我从mtl转换为Polysemy的第一个效果是日志记录,最初我使用的是simple-log,因为我喜欢能够在“作用域”中运行代码区域,当时co-log-polysemy是唯一现有的日志记录框架我打算使用它,但是我找到了di并决定为其编写Polysemy效果。

效果定义如下:

我继续编写一个解释器,该解释器使用Di中的现有框架来打印日志,我发现编写起来很简单,因为它很像主要包括玩各种类型的拼图游戏:

用于Log和的处理程序Flush非常简单,只需嵌入IO操作并包装结果,然后使用包含修改后的记录器状态运行嵌套操作的用于Push和处理程序就Attr可以了,这差不多可以了Reader ,我可能会重写它以重新解释Di效果就...而言Reader。

但是,此解释器需要Di.Core.Di从某处获取a ,而执行该3的唯一位置是使用具有签名的Di.Core.new:

这MonadMask约束手段,我们不能只用一词多义的Semr 单子,我的第一个决议,这是为了复制源new和替换 Control.Exception.Safe.finally与一词多义的4Resource.finally

这种方式需要太多的黑客来满足我的喜好,因此我花了一些时间弄清楚如何降低Member (Embed IO) r => Sem r ato IO a,幸运的是,该Resource效果已经做了很多我想做的事情,因此我目前的解决方案是使用单一操作:

并定义一个解释器:

此效果仅在以下实现中内部使用runDiToIO:

我不确定这是否是执行将Sem monad降级为IO的仪式的最佳方法,但是我看不到没有临时效果的任何执行方法。

无论如何,在编写解释器之后,可以编写帮助程序函数,它们是相当重复的,因此我只包括前几个:

如果要使用Polysemy.Plugin,则通常不需要手动类型的应用程序 ,但是当haddock(GHC 8.6.5)尝试在启用了插件的情况下构建文档时会死掉。

用法

现在记录器效果已编写完毕,我们可以像这样使用它:

产生以下内容:

打开APP阅读更多精彩内容