Во-первых, быстрый отказ от ответственности: «регистрация» обычно не имеет смысла в общем коде на Haskell, поскольку предполагает некоторое своего рода последовательное выполнение, которое может иметь или не иметь смысл. Убедитесь, что вы различаете протоколирование того, как программа выполняет и протоколирование того, какие значения вычисляются . В строгих императивных языках они в основном одинаковы, но в Хаскеле это не так.
Тем не менее, звучит так, как будто вы хотите вести журнал на основе вычисляемых значений в контексте уже последовательных вычислений и вычислений с учетом состояния, что в значительной степени работает так же, как ведение журнала в большинстве других языков. Тем не менее, вам нужна монада для поддержки некоторых средств для этого. Похоже, что используемый вами синтаксический анализатор из пакета HCodecs , который выглядит относительно ограниченным, не допускает IO
и не определяется как преобразователь монад.
Честно говоря, мой совет - рассмотреть возможность использования другой библиотеки разбора. Parsec имеет тенденцию быть выбором по умолчанию, и я думаю, что attoparsec популярен для определенных целей (которые могут включать в себя то, что вы делаете). Любой из них позволил бы добавлять журналирование гораздо проще: Parsec - это монадный преобразователь, поэтому вы можете поместить его поверх IO
, а затем использовать liftIO
по мере необходимости, тогда как attoparsec разработан для инкрементной обработки, поэтому вы можете разделить свои входные данные и регистрировать аспекты обработки (хотя регистрация внутри фактического синтаксического анализатора может быть более неудобной). Есть и другие варианты, но я не знаю достаточно деталей, чтобы дать рекомендацию. Большинство библиотек, основанных на комбинаторе синтаксических анализаторов, имеют довольно схожий дизайн, поэтому я ожидаю, что перенос вашего кода будет простым.
Последний вариант, если вы действительно хотите придерживаться того, что у вас есть, - это посмотреть на реализацию библиотеки синтаксического анализа, которую вы используете сейчас, и развернуть свою собственную IO
- ориентированная версия этого. Но это, вероятно, не идеально.
Кроме того, в качестве дополнения, если вы, что вы действительно ищете, на самом деле не регистрируют, а просто отслеживают выполнение вашей программы как часть разработки, вы можете найти отладчик, встроенный в GHCi, более полезным или хорошим отладка старомодного printf через модуль Debug.Trace .
Редактировать : Ладно, похоже, у вас есть правдоподобные причины подумать о том, чтобы выбрать свой вариант. То, что вы примерно хотите здесь, это ParserT
монадный трансформатор. Вот текущее определение Parser
:
newtype Parser a = Parser { unParser :: S -> Either String (a, S) }
Тип S
является состоянием синтаксического анализатора. Обратите внимание, что это примерно жестко закодированная версия StateT S (Either String) a
:
newtype StateT s m a = StateT { runStateT :: s -> m (a,s) }
... где Either String
обрабатывается как монада ошибок. ErrorT
монадный трансформатор делает то же самое:
newtype ErrorT e m a = ErrorT { runErrorT :: m (Either e a) }
Так что, где текущий тип эквивалентен StateT S (ErrorT String Identity)
, вы хотите получить StateT S (ErrorT String IO)
.
Похоже, что большинство функций в модуле не связываются с внутренностями монады Parser
, поэтому должен иметь возможность просто заменить определения типов, предоставив соответствующий класс типов экземпляры, напишите свою собственную функцию runParser
и будьте хороши.