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

Как мне зарегистрироваться в Haskell?

Я пытаюсь использовать HSlogger для получения некоторой информации о моей программе. Поэтому я добавляю следующую строку к моей функции

import Data.Word
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as L
import Data.Bits
import Data.Int
import Data.ByteString.Parser

import System.Log.Logger
import System.Log.Handler.Syslog


importFile :: FilePath -> IO (Either String (PESFile ))
importFile n = do
     warningM "MyApp.Component2" "Something Bad is about to happen."
     ...

И это прекрасно работает, потому что функция находится внутри IO. Однако, когда я добавляю подобную строку к следующей функции:

...
parsePES :: Parser PESFile
parsePES = do
        header <- string "#PES"
        warningM "parsing header"
        ...
        return (PESFile ...)

Я получаю ошибку типа:

 Couldn't match expected type `Parser a0'
                with actual type `String -> IO ()'
    In the return type of a call of `warningM'
    In a stmt of a 'do' expression: warningM "parsing header"
    In the expression:
      do { header <- string "#PES";
           warningM "parsing header";
        ...

И я полностью понимаю, почему - parsePES находится в монархе Parser, а не в монаде IO. Я не понимаю, что с этим делать. Нужен ли мне монадный трансформатор, чтобы я мог складывать монаду Parser и монаду IO вместе? Как мне это сделать?

4b9b3361

Ответ 1

Во-первых, быстрая оговорка: "регистрация" обычно не имеет смысла вообще в коде Haskell, потому что она предполагает какое-то последовательное выполнение, которое может или не может быть значимым. Убедитесь, что вы различаете ведение журнала, как программа выполняет и регистрирует, какие значения вычисляются. В строгих императивных языках они в основном одни и те же, но в 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, и будет хорошо идти.

Ответ 2

Отказ от ответственности: я являюсь автором структуры haskell Logger.

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

  1. Это медленно. Под медлительностью я подразумеваю, что каждый раз, когда вы регистрируете сообщение, оно анализирует (очень простым способом) строку, описывающую происхождение журнала, и использует некоторые экзистенциальные типы данных под капотом, которые должны вводить некоторые издержки производительности во время выполнения.
  2. Он не позволяет входить в другие монады, кроме IO, поэтому вы должны использовать WriterT или другие решения, чтобы не испортить ваш код.
  3. Он не расширяемый - вы не можете создавать свои собственные уровни приоритетов, определять пользовательское поведение (например, ведение журнала между потоками) или фильтровать журналы времени компиляции.
  4. Он не предоставляет некоторую информацию, такую как номера строк или имена файлов, в которые были помещены журналы. И, конечно, очень сложно расширить его для поддержки такой информации.

При этом я хотел бы представить каркас Logger haskell. Это позволяет для эффективной и расширяемой регистрации, в том числе:

  1. регистрация в последовательном чистом коде (выполнение так же как использование монады WriterT)
  2. расширенная фильтрация сообщений (включая фильтрацию во время компиляции)
  3. возможность регистрации между потоками
  4. предоставляет интерфейс TemplateHaskell позволяющий регистрировать дополнительные детали, такие как номера файлов или имена модулей
  5. очень легко расширяемый - все функции создаются как расширения простого BaseLogger, который не может сделать ничего разумного. Чтобы быть ясным - функциональность фильтрации создается менее чем за 20 строк в виде логгера-преобразователя, и вы можете определить свои собственные преобразователи. Как это сделать описано в документации.
  6. По умолчанию обеспечивает цветной вывод на всех платформах.

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

Регистратор разработан внутри компании, в которой я работаю (luna-lang.org), и используется внутри компилятора, который мы создаем.

Ответ 3

Бесстыдный плагин: я являюсь автором библиотеки co-log регистрации. Вы можете найти подробную информацию об использовании и реализации библиотеки в следующем сообщении в блоге:

Основная идея этой библиотеки состоит в том, чтобы рассматривать действия по протоколированию как простую функцию Haskell. Так как в Хаскеле работают первоклассные граждане, с ними очень легко работать.