ByteStrings в Haskell: следует ли использовать Put или Builder?

Я смущен тем, что монада Put предлагает с помощью Builder напрямую, в Data.Binary. Я прочитал раздел Binary Generation в разделе "Работа с двоичными данными", и, похоже, вы должны использовать Put, но он довольно короткий, t объясните почему.

Data.Binary.Put

Монаха Пута. Монада для эффективного построения ленивых байтов.

type Put = PutM ()

Поместите просто подъемник Builder в монадию писателя, применимую к().

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 a Colour Word8 до 24 бит. Но тогда я также должен придерживаться заголовка, и я не уверен, как это сделать.

Является ли Builder скрытым за кулисами или зависит от этого? Является ли класс Binary только для (де) сериализации данных или для всех целей генерации двоичных данных?

Ответ 1

Прежде всего обратите внимание на концептуальную разницу. Строители предназначены для эффективного построения потоковых потоков, в то время как монада PutM действительно предназначена для сериализации. Поэтому первый вопрос, который вы должны задать себе, заключается в том, действительно ли вы сериализуете (чтобы ответить, спрашивайте себя, есть ли значимая и точная противоположная операция - десериализация).

В общем, я бы пошел с Builder для удобства, которое он предоставляет. Однако не Builder из двоичного пакета, а на самом деле из пакета blaze-builder. Это моноид и имеет множество предопределенных генераторов строк. Это также очень сложно. Наконец, это очень быстро и на самом деле можно точно настроить.

И последнее, но не менее важное: если вы действительно хотите скорость, удобство и элегантный код, вы захотите объединить это с одной из различных библиотек потоков, таких как кабелепровод, перечислитель или каналы.

Ответ 2

Я вижу, что Put является монадой, тогда как Builder не является, но я действительно не понимаю, зачем понадобилось Put.

Если быть точным, PutM - это Monad. Это необходимо для удобства и предоставить вам меньше возможностей для ошибок. Написание кода в монадическом или аппликативном стиле часто намного удобнее, чем перенос всех временных ячеек в явном виде, и с помощью сантехники, выполненной в экземпляре Monad, вы не можете случайно использовать неправильный Builder в середине вашей функции.

Вы можете делать все, что вы делаете, с помощью PutM, используя только Builder, но обычно это больше работает для написания кода.

Но тогда я также должен придерживаться заголовка, и я не уверен, как это сделать.

Я не знаю формат PPM, поэтому я понятия не имею, как построить заголовок. Но после его построения вы можете просто использовать putByteString или putLazyByteString для его включения.

Ответ 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, напротив, действительно является Монадой и выигрывает от ясного понимания).