10

我对Putmonad 提供的Builder直接使用的内容感到困惑,在Data.Binary. 我阅读了处理二进制数据的二进制生成部分,似乎假设您应该使用Put,但它很短并没有解释原因。

数据.二进制.Put

Put 单子。用于有效构造惰性字节串的 monad。

type Put = PutM ()

只将 Builder 提升为 Writer monad,应用于 ()。

Data.Binary.Builder

懒惰字节字符串的高效构造。


Writer应用单子有什么意义()

我可以看到这Put是(类型同义词)一个单子,而Builder不是,但我真的不明白为什么Put需要。

就我而言,我正在渲染一个 3D 场景并将每个像素写入 3 个字节,然后将 PPM 格式的标题添加到开头(稍后将使用 PNG)。

Binary似乎它旨在为可以序列化和反序列化二进制数据的类型实例化。这不完全是我正在做的事情,但是Binary为我的颜色类型实例化感觉很自然

instance (Binary a) => Binary (Colour a) where
    put (Colour r g b) = put r >> put g >> put b
    get = Colour <$> get <*> get <*> get

这使得它很容易put变成Colour Word824 位。但是我还必须添加标题,我不知道该怎么做。

Builder要隐藏在幕后,还是取决于?该类Binary仅用于(反)序列化数据,还是用于所有二进制生成目的?

4

3 回答 3

11

首先注意概念上的差异。构建器用于高效构建字节串流,而PutMmonad 真正用于序列化。所以你应该问自己的第一个问题是你是否真的在序列化(回答这个问题时问问你自己是否有一个有意义且完全相反的操作——反序列化​​)。

一般来说,我会选择Builder它提供的便利。但是,不是Builder来自二进制包,而是来自blaze-builder包。它是一个幺半群,并有许多预定义的字符串生成器。它也是非常可组合的。最后,它非常快,实际上可以进行微调。

最后但并非最不重要的一点是,如果您真的想要速度、便利和优雅的代码,您将希望将其与各种流处理器库之一结合起来,如管道枚举器或管道

于 2012-07-16T21:55:10.493 回答
10

我可以看到这Put是一个单子,而Builder不是,但我真的不明白为什么Put需要。

准确地说,PutMMonad. 需要它来方便,并减少出错的机会。以 monadic 或 applicative 风格编写代码通常比显式携带所有临时代码要方便得多,并且在Monad实例中完成了管道,您不会Builder在函数中间意外使用错误。

您可以PutM使用 only完成所有操作Builder,但通常编写代码需要更多工作。

但是我还必须添加标题,我不知道该怎么做。

我不知道 PPM 格式,所以我不知道如何构造标题。但是在构建它之后,您可以简单地使用putByteStringputLazyByteString附加它。

于 2012-07-16T21:15:38.837 回答
3

我不确定这在多大程度上是准确的,但我的理解一直是,Put如您所见,它的表示在很大程度上是对 do-notation 的滥用,因此您可以编写如下代码:

putThing :: Thing -> Put
putThing (Thing thing1 thing2) = do
  putThing1 thing1
  putThing2 thing2

我们没有使用“本质” Monad(特别是,我们从不绑定任何东西的结果),但我们获得了一种方便且简洁的连接语法。然而,相对于纯单曲面替代方案的美学优势:

putThing :: Thing -> Builder
putThing (Thing thing1 thing2) = mconcat [
  putThing thing1,
  putThing thing2]

在我看来,是相当小的。

(请注意Get,相比之下,真正一个 Monad,并且以清晰的方式受益)。

于 2012-07-31T16:01:06.447 回答