Как мне войти в Haskell? - PullRequest
       1

Как мне войти в Haskell?

44 голосов
/ 10 июня 2011

Я пытаюсь использовать 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 вместе? Как мне это сделать?

Ответы [ 3 ]

50 голосов
/ 10 июня 2011

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

25 голосов
/ 18 января 2015

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

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

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

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

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

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

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

0 голосов
/ 04 февраля 2019

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

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

...