it-swarm.com.ru

Как мне войти в систему на 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 вместе? Как мне это сделать?

42
nont

Во-первых, быстрый отказ от ответственности: «регистрация» обычно не имеет смысла в общем коде на Haskell, поскольку предполагает некоторое своего рода последовательное выполнение, которое может иметь или не иметь смысл. Убедитесь, что вы различаете ведение журнала выполнения программы и ведение журнала того, какие значения вычислены. В строгих императивных языках они в основном одинаковы, но в Хаскеле это не так.

Тем не менее, звучит так, как будто вы хотите вести журнал на основе вычисляемых значений в контексте уже последовательных вычислений и вычислений с учетом состояния, что в значительной степени работает так же, как ведение журнала в большинстве других языков. Тем не менее, вам нужна монада для поддержки некоторых средств для этого. Похоже, что используемый вами синтаксический анализатор - из пакета HCodecs , который выглядит относительно ограниченным, не допускает IO и не определяется как преобразователь монад.

Честно говоря, мой совет - рассмотреть возможность использования другой библиотеки разбора. Parsec имеет тенденцию быть своего рода выбором по умолчанию, и я думаю, что attoparsec популярен для определенных целей (которые могут включать в себя то, что вы делаете). Любой из них позволил бы добавлять журналирование гораздо проще: Parsec - это монадный преобразователь, поэтому вы можете поместить его поверх IO, а затем использовать liftIO по мере необходимости, тогда как attoparsec разработан для инкрементной обработки, так что вы можете разделить входные и логические аспекты обработка (хотя регистрация внутри фактического парсера может быть более неудобной). Есть и другие варианты, но я не знаю достаточно деталей, чтобы дать рекомендацию. Большинство библиотек, основанных на комбинаторе синтаксических анализаторов, имеют довольно схожий дизайн, поэтому я ожидаю, что перенос вашего кода будет простым.

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


Кроме того, в качестве дополнения, если вы, что вы действительно ищете, на самом деле не регистрируют, а просто отслеживают выполнение вашей программы как часть разработки, вы можете найти отладчик, встроенный в GHCi, более полезным или старомодным. отладка printf через модуль Debug.Trace .


Edit: Хорошо, звучит так, будто у вас есть правдоподобные причины подумать о том, чтобы применить свой вариант. Здесь вы примерно хотите получить преобразователь монад 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 и будь хорошим.

48
C. A. McCann

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

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

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

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

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

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

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

24
Wojciech Danilo

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

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

0
Shersh