Статьи

От OO до FP: Haskell I / O, часть 2

В моем посте части 1 на эту тему мы фактически сделали все операции ввода-вывода, которые я собираюсь сделать здесь. Мы лениво читаем весь пример файла данных, файл, содержащий данные, описывающие события, генерируемые монитором процесса. Моя следующая цель состояла в том, чтобы повторно гидрировать мои события из строки, сериализованной в файл. Эти строки s были получены с помощью вызова функции шоу в моем списке из событий s.

Время немного подкрепиться. Мы получили шоу бесплатно, но мы должны были попросить его. Оставив на некоторое время тип данных Event и остановившись на более простом типе данных Property , вспомните, как мы его определили:

     data Property = Property {
     key :: Key,
     value:: Value }
     deriving (Show)

В то время мы обсуждали «
получение (показ) », просто отметив, что это позволяет
ghci знать, как выводить
значения
свойств . Конечно, это еще не все. «Больше» — это классы типов Haskell. Класс типов Haskell определяет набор функций, которые могут быть определены для работы с типом данных. Функция
show является членом класса
типов
Show и имеет следующий тип:

     *EventProcessor> :type show
   show :: Show a => a -> String

На мгновение посмотрим, что произошло, если мы не указали, что
Property наследует
Show :

     -- file:  c:/HaskellDev/eventProcessor/simpleProperty.hs
  type Key = String
  type Value = String
  data Property = Property {
    key :: Key,
    value:: Value }

Если мы загрузим этот файл, затем создадим
свойство , а затем попробуем его
показать , мы увидим следующее:

     Prelude> :load simpleProperty.hs
   [1 of 1] Compiling Main             ( simpleProperty.hs, interpreted )
   Ok, modules loaded: Main.
   *Main> let prop1 = Property "key1" "value1"
   *Main> show prop1
   :1:1:
   No instance for (Show Property)
     arising from a use of `show'
   Possible fix: add an instance declaration for (Show Property)
   In the expression: show prop1
   In an equation for `it': it = show prop1

Это значительно более полезно, чем типичное сообщение об ошибке компилятора. Ключ подсказка для добавления объявления экземпляра. Чтобы функции класса типов были применимы к вашему типу данных, вам необходимо объявить экземпляр этого класса типов, который обрабатывает ваш тип данных. В нашем случае мы можем получить
Show, просто заявив, что мы его выводим. Итак, мы вернемся к нашему старому определению:

    -- file:  c:/HaskellDev/eventProcessor/simpleProperty.hs
   type Key = String
   type Value = String
   data Property = Property {
     key :: Key,
     value:: Value }
     deriving (Show)

Повторяя наш предыдущий тест, теперь мы получаем следующее:

    *Main> :load simpleProperty.hs
   [1 of 1] Compiling Main             ( simpleProperty.hs, interpreted )
   Ok, modules loaded: Main.
   *Main> let prop1 = Property "key1" "value1"
   *Main> show prop1
   "Property {key = \"key1\", value = \"value1\"}"

Это определенно улучшение. Но вернемся к теме — я хочу прочитать
строку и передать ее в
свойство . Во-первых, давайте попробуем прочитать
строку в
целое число :

     *Main> read "5"
   :1:1:
   Ambiguous type variable `a0' in the constraint:
   Read a0) arising from a use of `read'
   Probable fix: add a type signature that fixes these type variable(s)
   In the expression: read "5"
   In an equation for `it': it = read "5"

Опять же, полезный совет от
ghci . По сути, он не знает, что мы пытаемся создать, и предлагает добавить сигнатуру типа. Вот как бы вы это сделали:

     *Main> let anInt = (read "5")::Integer
   *Main> :type anInt
   anInt :: Integer
   *Main> show anInt
   "5"

Теперь Haskell знает, что мы пытаемся прочитать
Integer , поэтому он распознает, что
anInt является
Integer, и знает, как его
показать . Все это должно сказать вам, что
Integer определил экземпляры для
Show и
Read .

Можем ли мы получить
Read «бесплатно», просто заявив, что наш тип данных получает его? Не больно пытаться:

   -- file:  c:/HaskellDev/eventProcessor/simpleProperty.hs
   type Key = String
   type Value = String
   data Property = Property {
     key :: Key,
     value:: Value }
     deriving (Read, Show)

Далее я создам
свойство и выводит его с
шоу , а затем увидеть , если этот формат (который, кстати, как я создал свой образец
Event файл — с помощью шоу на
список из
событий s) является «
чтения » -able:

   *Main> let prop1 = Property "key1" "value1"
   *Main> show prop1
   "Property {key = \"key1\", value = \"value1\"}"
   *Main> let prop2 = (read "Property {key = \"key1\", value = \"value1\"}")::Property
   *Main> show prop2
   "Property {key = \"key1\", value = \"value1\"}"

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

То , что я бы очень хотел , чтобы сделать это , чтобы быть в состоянии прочитать полное
событие , как это определено в моих предыдущих сообщений, в том числе
список из
событий s. Вот пример
события :

     "Event {timestamp = 1320512200548, className = \"java.lang.String\", lineNumber = 1293, 
message = \"NPE in substring()\", properties = [Property {key = \"userId\", value = \"smith\"},
Property {key = \"sessionId\", value = \"ABCD1234\"}]}"

Давайте попробуем тот же трюк с
Event, который мы использовали с
Property :

  -- file:  c:/HaskellDev/eventProcessor/notSoSimpleProperty.hs

   type Timestamp = Integer
   type ClassName = String
     type LineNumber = Integer
     type Message = String
     type Key = String
     type Value = String
     type Properties = [Property]

     data Property = Property {
       key :: Key,
       value:: Value }
       deriving (Read, Show)

     data Event = Event {
       timestamp :: Timestamp,
       className :: ClassName,
       lineNumber :: LineNumber,
       message :: Message,
       properties :: Properties }
       deriving (Read, Show)

Достаточно ли было просто объявить, что
Событие происходит от
Show ? Давайте посмотрим:

     *EventProcessor> :load notSoSimpleProperty.hs
   [1 of 1] Compiling Main             ( notSoSimpleProperty.hs, interpreted )
   Ok, modules loaded: Main.
   *Main> let event1 = (read "Event {timestamp = 1320512200548, className = \"java.lang.String\", 
lineNumber = 1293, message = \"NPE in substring()\", properties = [Property {key = \"userId\", value = \"smith\"},
Property {key = \"sessionId\", value = \"ABCD1234\"}]}")::Event *Main> show event1 "Event {timestamp = 1320512200548, className = \"java.lang.String\", lineNumber = 1293, message = \"NPE in substring()\",
properties = [Property {key = \"userId\", value = \"smith\"},Property {key = \"sessionId\", value = \"ABCD1234\"}]}" *Main> show (properties event1) "[Property {key = \"userId\", value = \"smith\"},Property {key = \"sessionId\", value = \"ABCD1234\"}]"

Это здорово. Мы можем взять
строковое представление
события как вывод от
show и использовать его непосредственно с
read для создания экземпляра
переменной
Event .

Это обсуждение никоим образом не описывает, как предоставить экземпляр класса типов; возможно в другом посте. Сейчас я все еще хочу создать
Event s из моего примера
файла
Event .

Мое первое сокращение в этом заключается в следующем:

   import System.IO
   import EventProcessor

   main :: IO ()
   main = do
    inh <- openFile "sampleEvents" ReadMode
    inContents <- hGetContents inh
    let eventList = processData (lines inContents)
    hClose inh

   processData :: [String] -> [Event]
   processData = map createEvent

   createEvent :: String -> Event
   createEvent event = (read event)::Event

В то время как это кажется правильным, это ничего , что доказывает , что я смог разобрать мои строки примера-файлы в не сделать
список из
событий s. К сожалению, на данный момент я немного застрял, так как я еще не понял, как правильно извлекать элементы из
событий Event s (я могу сделать это в интерактивном режиме в
ghci , но меня мучают ошибки компилятора, если я пытаюсь это сделать так в
блоке
do ).

Я собираюсь оставить это на некоторое время и вернуться к некоторым онлайн-ресурсам / учебным пособиям, а затем вернуться к ним. Проблема для меня в том, что я до сих пор не понимаю монаду ввода / вывода, как она называется. Мне становится ясно, что концепция монады — очевидно, настолько неотъемлемая для Хаскеля — нелегко понять, о чем свидетельствует подавляющее количество онлайн-уроков «Вот мой взгляд на монады» (одного автора, я вижу шутит, что каждый, кто узнает о монадах, вскоре после этого публикует учебник по этой теме). Поэтому я отправляюсь на Haskell Wiki, чтобы узнать о монадах, затем узнать больше о вводе-выводе Haskell, а затем продолжить с того места, где я остановился.

 

С http://wayne-adams.blogspot.com/2011/12/from-oo-to-fp-haskell-io-part-2.html