Подтвердить что ты не робот

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 только для (де) сериализации данных или для всех целей генерации двоичных данных?

4b9b3361

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