ChatGPT解决这个技术问题 Extra ChatGPT

mtl、transformers、monads-fd、monadLib 和选择悖论

Hackage 有几个用于 monad 转换器的包:

mtl: Monad 转换器库

变压器:具体函子和单子变压器

monads-fd:Monad 类,使用函数依赖

monads-tf:Monad 类,使用类型族

monadLib:monad 转换器的集合。

mtl-tf:使用类型族的 Monad 转换器库。

mmtl:模块化 Monad 变压器库

mtlx:具有类型索引的 Monad 转换器库,提供“免费”副本。

compose-trans:可组合的 monad 转换器

(也许我错过了一些)

我们应该使用哪一个?

mtl 是 Haskell 平台中的一个,但我一直在 reddit 上听到它不酷。

但是无论如何选择有什么不好的,这不只是一件好事吗?

好吧,我看到了例如 data-accessor 的作者如何不得不做出所有这些来迎合流行的选择:

data-accessor-monadLib 库:monadLib 的 monad 的访问器函数

data-accessor-monads-fd 库:使用 Accessor 访问 monads-fd State monad 类中的状态

data-accessor-monads-tf 库:使用 Accessor 访问 monads-tf State monad 类型族中的状态

data-accessor-mtl 库:使用 Accessor 访问 mtl State monad 类中的状态

data-accessor-transformers 库:使用 Accessor 访问变压器状态单子中的状态

我想如果这种情况继续下去,例如几个相互竞争的 Arrow 包演变,我们可能会看到类似的东西:spoonklink-arrows-transformers、spoonklink-arrows-monadLib、spoonklink-tfArrows-transformers、spoonklink-tfArrows-monadLib,...

然后我担心如果spoonklink 被分叉,Hackage 会耗尽磁盘空间。 :)

问题:

为什么有这么多单子变压器包?

为什么 mtl [认为] 不酷?

主要区别是什么?

大多数这些看似相互竞争的软件包都是由 Andy Gill 编写的,并由 Ross Paterson 维护。这是否意味着这些软件包不是相互竞争而是以某种方式协同工作? Andy 和 Ross 是否认为他们自己的软件包已经过时?

你和我应该使用哪一个?

此链接帮助我了解 mtl 与转换器 haskell.org/haskellwiki/Monad_Transformer_Library
向下滚动查看 @jberryman comment!使用 mtl 或变压器,它们变得兼容!

i
imz -- Ivan Zakharyaschev

其中一堆几乎完全等价:

mtl 使用 GHC 扩展,但转换器是 Haskell 98。

monads-fd 和 monads-tf 是转换器的附加组件,分别使用函数依赖和类型族,两者都提供了转换器中缺少的 mtl 功能。

mtl-tf 是使用类型族重新实现的 mtl。

所以本质上,mtl == transformers ++ monads-fdmtl-tf == transformers ++ monads-tf。我认为,transformers 及其相关软件包的改进的可移植性和模块化是 mtl 现在不酷的原因。

mmtlmtlx 似乎都与 mtl 相似和/或基于 mtl,但有 API 差异和额外功能。

MonadLib 似乎对事情有相当不同的看法,但我并不直接熟悉它。似乎也使用了很多 GHC 扩展,比其他扩展更多。

乍一看,compose-trans 似乎更像是用于创建 monad 转换器的元编程材料。它声称与 Control.Monad.Trans 兼容...我猜这意味着 mtl

无论如何,我建议使用以下决策算法:

你需要一个新项目的标准单子吗?使用transformers & co.,帮助我们让mtl休息。

您是否已经在大型项目中使用 mtl?变压器并不完全兼容,但没有人会因为不切换而杀死你。

其他软件包之一是否提供了您需要的不寻常功能?还不如使用它而不是自己滚动。

还是不满意?把它们全部扔掉,下载额外的类别,用一页半难以理解的抽象废话惊人的通用代码解决世界上所有的问题。


如果mtl == transformers ++ monads-fd,就不能这样实现吗? (作为替换它的一个阶段),这将不需要拥有像 data-accessor-mtl 这样的东西
@yairchu:是的,但是你希望我怎么做呢? :) 保持向后兼容性从未像看起来那么容易,更改关键库需要时间、精力和一定程度的社区支持。 monad 转换器的情况是一个已知问题,但我认为这不是任何人的首要任务。
@yairchu:这基本上就是正在做的事情。 mtl 的下一个主要版本应该是导入transformers + monads-fd 的存根,与该版本的兼容性将是决定因素。然后,库将能够单独更新,以便它们与 mtl 1.1 和 1.2 兼容,然后应用程序会被归入已安装的版本或它们最严格的库依赖项所需的版本。
图书馆邮件列表目前正在讨论将 MonadIO(可能还有 MonadTrans 从 mtl 移到 base 中)。尽管“MonadBase”需要 MPTC、fundeps 等,但是否提取 MonadIO 或更通用的 MonadBase 已经挂了.
因为我发现这篇文章非常有用。我想我会更新其他 googlers:mtl 现在依赖于转换器,monads-fd 现在是 mtl 周围的存根。因此,如果您需要它拥有的额外好东西,请使用 mtl,或者如果它拥有您需要的一切,则只需导入转换器。
E
Edward Kmett

暂时?您可能应该使用 mtl。正在发生的事情是 transformers 库以 monads-fdmonads-tf 可以和平共存的方式从 MTL 中分离出来,但最后检查情况并非如此。

发生这种情况时,您将能够导入 monads-fdtransformers 并获得(几乎)相同的界面,除了 State 等将成为 StateT 的别名。

所以我会写信给 mtl,但不要依赖 State、Reader 等当前是 data 的事实,因为它们将被 type 替换。

MonadLib 是 Iavor 一直在研究的另一种替代方案,可以安全使用,因为它不与其他模块共享任何模块名称,但使用模式完全不同。


在什么意义上共存?由同一个包使用?导入同一个模块?合并到同一个变压器堆栈中?一般来说,混合基金和 TF 让我觉得是个坏主意。无论如何,我没有大量使用 transformers &公司但是,在切换一些(相当简单的)代码时,除了一些小的 API 差异与 mtl 之外,没有注意到任何问题。
问题归结为您只能加载一个提供给定模块的包。因此,如果您使用使用 mtl 的库,即使在内部,您也无法导入替代方案。目前,有相当比例的 hackage 在内部以某种方式使用 mtl。许多人更喜欢使用类型族,而 monads-tf 为他们提供了这一点,但请记住,目前,在 transformers + monads-fd 重构完成之前,这将锁定该代码,使其无法使用任何传递需要 MTL 的库.这包括一些相当大的门票项目。
从长远来看,使用transformers+monads-(tf|fd) 将避免那种泡菜,但我们还没有。与此同时,使用的优势有利于 mtl。升级路径看起来是 mtl 的下一个主要版本将被重新定义为导入 monads-fd 和转换器的存根。主要版本中断提供了一种在您的 cabal 文件中声明您不关心您获得哪个版本(即您不关心 State 是类型别名或数据类型)的好方法,并且一旦发生主要版本碰撞然后您不必关心您使用的每个库是否都具有相同的偏见。
所以,最终你到目前为止的经验正是transformers/monads-(tf|fd) 旨在支持的。但是,在编写它们之后意识到的是,社区在切换库方面非常糟糕,因为没有令人信服的理由跳出,也没有大量的遗留理由留下来。因此需要重新定义 mtl 并明确升级路径。
太棒了,感谢您的详细说明!显然,我切换的代码几乎没有外部依赖项——我认为主要是 FFI 绑定。我也没有意识到模块名称冲突是……侵入性的,我猜?这确实让事情变得尴尬。 :(
C
Community

Edward Kmett 在 his answer 中提到的分解已于 2010 年底完成。其最终结果是 monads-fd,基于 transformers,成为 mtl 的第 2 版。由于 mtl 无处不在,monads-tf 从未真正流行起来。截至 2017 年初,mtltransformers 是唯一被广泛使用的 monad 转换器库。