Лучшая практика с Monad

Я хотел бы знать, что можно считать лучшей практикой в ​​отношении монады State. Я также открыт для любых других предложений.

У меня есть бинарный файл для разбора. Он содержит разные заголовки, которые необходимо проанализировать, чтобы иметь возможность прочитать весь файл.

Таким образом, заголовки можно анализировать, используя только состояние из анализа.

data ParseState = ParseState {
   offset :: Int64
   buffer :: B.ByteString
   endianness :: Endianness
   pointerSize :: MachineWord
   positionStack :: [Int64]
}

Затем эти данные используются в State монаде.

type Parser a = State ParseState a

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

data Header = Header {
    txtOffset :: Int64,
    stringOffset :: Int64
}

Мне нужна информация заголовка, чтобы продолжить синтаксический анализ файла.

Моя идея состояла в том, чтобы использовать новую монаду состояния, которая находится поверх предыдущей. Итак, у меня есть новая монада StateT:

type ParserFullState a = StateT Header (State ParserState) a

Таким образом, я могу продолжить и построить целый набор функций парсера, используя новый преобразователь состояния. Я также мог бы сделать это по-другому и добавить заголовок к исходным данным ParseState.

Плюсы, которые я вижу при добавлении заголовка обратно в ParserState, следующие:

  1. Тип возвращаемого значения функции синтаксического анализатора — универсальный.
  2. Нет необходимости вызывать lift для доступа к примитиву парсера.

Минусы я вижу следующие:

  1. Нет различия между синтаксическим анализатором более высокого уровня и примитивом более низкого уровня.
  2. Мы не можем четко сказать, когда заголовок полностью проанализирован, а когда нет. Таким образом, модификация парсера становится более хрупкой.

Каково ваше предложение? Должен ли я использовать преобразователь состояния, должен ли я добавить заголовок в исходное состояние или что-то еще?

Спасибо.


person mathk    schedule 14.08.2014    source источник
comment
Почему бы не data ParseState' = Initial ParseState | Later ParseState Header или какой-либо другой АТД, который подходит для вашей задачи, чтобы у вас было только одно состояние, которое может представлять ваше начальное состояние синтаксического анализа, а затем, когда вам понадобится Header, у вас есть информация как ParseState, так и Header, и они хранятся отдельно? Вам просто нужно put (Later parseState header), когда у вас есть информация заголовка.   -  person bheklilr    schedule 14.08.2014


Ответы (1)


Как правило, я бы не советовал использовать несколько слоев State (или вообще любого трансформатора). Преобразователи прекрасны, но в более толстых кластерах они сбивают с толку, особенно когда система типов не может правильно решить, какой MonadState использовать больше.

Тем не менее, в вашем конкретном случае другой преобразователь на самом деле является хорошей идеей, но не StateT: информация заголовка не должна меняться при дальнейшем анализе файла, поэтому на самом деле это должен быть просто ReaderT, не так ли?

type ParserFullState = ReaderT Header (State ParserState)

или эквивалентно

type ParserFullState = RSS Header () ParserState
person leftaroundabout    schedule 14.08.2014
comment
Обратите внимание, что трансформаторы сбивают с толку только в том случае, если вы используете библиотеку mtl. Если вы используете библиотеку transformers с явными lift, это просто. - person Gabriel Gonzalez; 14.08.2014
comment
@GabrielGonzalez: Система типов не сбивает с толку, но программиста она может сбить с толку! - person Tikhon Jelvis; 15.08.2014