将函数包装到 monad 转换器中是否/应该被视为不好的做法?

发布于 2024-10-05 09:47:35 字数 624 浏览 4 评论 0原文

假设我们想要在 Maybe monad 上使用 ReaderT [(a,b)],然后我们想要在列表中进行查找。

现在,一个简单且不太罕见的方法是:

第一种可能性

find a = ReaderT (lookup a)

然而,这似乎确实断言了有关 ReaderT 转换器如何工作的一些不平凡的事情。查看 Control.Monad.Reader 的源代码,很明显这工作得很好。但我还没有阅读任何支持这一点的文档。然而,我们也可以这样写 find:

第二种可能性

find a = do  y <- ask 
             lift (lookup a y)

类似的想法也适用于包装 MaybeTStateTState阅读器。通常我会写类似第一个示例的内容,但大多数时候,如何像第二个示例一样编写它是非常明显的,您甚至可能会说它更具可读性。所以我的问题是:像第一个示例这样的代码应该被认为是不好的吗?

Let's say we want to use ReaderT [(a,b)] over the Maybe monad, and then we want to do a lookup in the list.

Now an easy, and not too uncommon way to this is:

first possibility

find a = ReaderT (lookup a)

However it does seem like this asserts some non-trivial thing about how the ReaderT transformer works. Looking at the source code for Control.Monad.Reader it's clear that this works just fine. But I haven't read any documentation supporting this. However we could also write find like this:

second possibility

find a = do  y <- ask 
             lift (lookup a y)

Similar ideas hold for wrapping MaybeT, StateT, State and Reader. Usually I write something like the first example, but most of the time it is really obvious how to write it like the second example, and you might even say it's more readable. So my question is: should code like the first example be considered bad?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

半窗疏影 2024-10-12 09:47:35

我认为第一种方式最大的问题是:

如果 mtl 作者(或您使用的任何转换器库)决定停止导出 ReaderT 的数据构造函数,那么它将停止工作。这种情况发生在 State monad 从 mtl 1 到 mtl 2 的版本升级中,这非常烦人。然而,ask 是 Reader 官方 API 的一部分,您应该计划保留它。

另一方面,我不认为第一种方法是错误的。

I think the biggest problem with the first way is:

If the mtl authors (or whatever transformer library you use), decide to stop exporting the data constructor for ReaderT then it will stop working. This happened with the State monad in the version bump from mtl 1 to mtl 2 and it's quite annoying. Whereas, ask is part of the official api of Reader and you should plan on it sticking around.

On the other hand, I wouldn't consider the first way wrong.

谁许谁一生繁华 2024-10-12 09:47:35

mtl 库的当前版本 - 基于 transformers 库 - 导出函数 reader :: (r -> a) ->当使用简单的 Reader monad 时,Reader r a 正是为了这个目的。所以我们看到库的设计确实考虑到了这种用法。由于 ReaderT 没有提供这样的函数,因此我们可以有信心地说,官方支持的使用 ReaderT 执行此操作的方法是直接使用构造函数。

如果你说类似的 readerT :: Monad m => ,我会同意你的观点。 (r→a)→ ReaderTM a 应该添加到库中。这对于一致性和允许有一天在不破坏任何人的代码的情况下更改内部表示的可能性都是有好处的。

但就目前而言,“第一种可能性”是要走的路。

The current version of the mtl library - which is based on the transformers library - exports the function reader :: (r -> a) -> Reader r a for exactly this purpose when using the simple Reader monad. So we see that the design of the library does take into account this usage. Since no such function is provided for ReaderT, we can say with some confidence that the officially supported way to do this with ReaderT is to use the constructor directly.

I'll agree with you if you say that an analogous readerT :: Monad m => (r -> a) -> ReaderT r m a ought to be added to the library. That would be good both for consistency, and for allowing the possibility of changing the internal representation someday without breaking anyone's code.

But for now, your "first possibility" is the way to go.

哭泣的笑容 2024-10-12 09:47:35

至少速度上有差距。

我写了一个程序,它使用随机生成作为状态,并且在运行时必须生成大约5000000个随机值。现在考虑这两个掷骰子的函数:

random16  = State $ randomR (1,6) -- Using the internal representation
random16' = do
            s <- get
            (r,s') <- randomR (1,6) s
            put s'
            return r

第一个函数的程序运行时间约为 6 秒,而第二个函数则慢得多,大约需要 8 秒。我可以想象,它对于读者来说是相似的,所以当运行时很重要时,也许可以使用这个而不是更清晰的。我为此使用了严格版本。

At least there is a speed difference.

I wrote a program, which uses a random gen as a state and must generate about 5000000 random values while running. Now consider these two functions, which roll a dice:

random16  = State $ randomR (1,6) -- Using the internal representation
random16' = do
            s <- get
            (r,s') <- randomR (1,6) s
            put s'
            return r

Whith the first one, the program runs in about 6 seconds, while the second one is much slower, taking about 8 seconds. I can image, that it is similar for reader, so maybe use this one instead of the more clearer when runtime is important. I used the strict version for this.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文