【问题标题】:ByteStrings in Haskell: should I use Put or Builder?Haskell 中的 ByteStrings:我应该使用 Put 还是 Builder?
【发布时间】:2012-07-16 20:49:12
【问题描述】:

我很困惑Put monad 在Data.Binary 中直接使用Builder 提供了什么。我阅读了处理二进制数据的Binary Generation 部分,似乎假设您应该使用Put,但它很短并不能解释原因。

Data.Binary.Put

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

type Put = PutM ()

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

Data.Binary.Builder

懒惰字节串的高效构造。


Writer monad 应用于 () 有什么意义?

我可以看到 Put 是 monad(类型同义词),而 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 Word8 转换为24 位变得容易。但是后来我还必须添加标题,我不知道该怎么做。

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

【问题讨论】:

  • 不是答案,但您可能想看看使用 blaze-builder(和朋友)而不是二进制文件。
  • @TiloWiklund:我以前见过。它有什么不同?效率更高吗?
  • 除其他外。这里的一位作者写得很好:lambda-view.blogspot.se/2010/11/…
  • Binary 类及其方法Get 和Put 本质上是用于序列化和反序列化Haskell 结构。如果您正在使用现有的二进制格式,最好(在我看来,当然)避免使用 Binary 类,而是使用 putWord8、putWord32le 等显式函数。
  • @stephentetley:我也是这么想的,但我不确定……至少对于颜色,使用Put 很方便,并且在语义上也有意义,但我不知道我是否应该将Put 用于所有内容。

标签: haskell functional-programming monads binary-data


【解决方案1】:

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

一般来说,为了方便起见,我会选择Builder。但是,不是 binary 包中的 Builder,而是 blaze-builder 包中的。它是一个幺半群,并有许多预定义的字符串生成器。它也是非常可组合的。最后,它非常快,实际上可以微调。

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

【讨论】:

    【解决方案2】:

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

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

    您可以只使用Builder 完成对PutM 所做的所有事情,但通常编写代码需要更多的工作。

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

    我不知道 PPM 格式,所以我不知道如何构造标题。但是在构建之后,你可以简单地使用putByteString 或putLazyByteString 来添加它。

    【讨论】:

    • 您是否同意 Put 仅用于序列化和反序列化,并且(例如)将数据单向呈现为二进制图像(作为 ByteString)最好只使用 @987654334 @(可能是 blaze-builder),正如其他人所说的那样?
    • 我会说put 和get 是,但Put 和Get 可以合理地用于更广泛的范围。然而,binary 的主要目的确实是对 Haskell 结构进行(反)序列化,因此为此设计了接口。因此 blaze-builder 可能是更好的选择(我从未使用过,所以我不知道它使用起来有多方便)。
    【解决方案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,并且以清晰的方式受益)。

    【讨论】:

      猜你喜欢
      • 2014-08-06
      • 2014-11-06
      • 2018-01-09
      • 1970-01-01
      • 2018-09-05
      • 2017-11-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多