Является ли разложение стрелки из обозначения стрелки действительным преобразованием?

Я пытаюсь разобраться в HXT, библиотеке Haskell для разбора XML, использующей стрелки. Для моего конкретного случая использования я бы предпочел не использовать deep, поскольку бывают случаи, когда <outer_tag><payload_tag>value</payload_tag></outer_tag> отличается от <outer_tag><inner_tag><payload_tag>value</payload_tag></inner_tag></outer_tag>, но я столкнулся с некоторыми странностями, которые, казалось, должны работать, но не работают.

Мне удалось придумать тестовый пример на основе этот пример из документации:

{-# LANGUAGE Arrows, NoMonomorphismRestriction #-}
module Main where

import Text.XML.HXT.Core

data Guest = Guest { firstName, lastName :: String }
  deriving (Show, Eq)


getGuest = deep (isElem >>> hasName "guest") >>> 
  proc x -> do
    fname <- getText <<< getChildren <<< deep (hasName "fname") -< x
    lname <- getText <<< getChildren <<< deep (hasName "lname") -< x
    returnA -< Guest { firstName = fname, lastName = lname }

getGuest' = deep (isElem >>> hasName "guest") >>> 
  proc x -> do
    fname <- getText <<< getChildren <<< (hasName "fname") <<< getChildren -< x
    lname <- getText <<< getChildren <<< (hasName "lname") <<< getChildren -< x
    returnA -< Guest { firstName = fname, lastName = lname }

getGuest'' = deep (isElem >>> hasName "guest") >>> getChildren >>>
  proc x -> do
    fname <- getText <<< getChildren <<< (hasName "fname") -< x
    lname <- getText <<< getChildren <<< (hasName "lname") -< x
    returnA -< Guest { firstName = fname, lastName = lname }


driver finalArrow = runX (readDocument [withValidate no] "guestbook.xml" >>> finalArrow)

main = do 
  guests <- driver getGuest
  print "getGuest"
  print guests

  guests' <- driver getGuest'
  print "getGuest'"
  print guests'

  guests'' <- driver getGuest''
  print "getGuest''"
  print guests''

Между getGuest и getGuest' я расширяю deep до правильного числа getChildren. Полученная функция все еще работает. Затем я факторизую getChildren за пределами блока do, но это приводит к сбою результирующей функции. Результат:

"getGuest"
[Guest {firstName = "John", lastName = "Steinbeck"},Guest {firstName = "Henry", lastName = "Ford"},Guest {firstName = "Andrew", lastName = "Carnegie"},Guest {firstName = "Anton", lastName = "Chekhov"},Guest {firstName = "George", lastName = "Washington"},Guest {firstName = "William", lastName = "Shakespeare"},Guest {firstName = "Nathaniel", lastName = "Hawthorne"}]
"getGuest'"
[Guest {firstName = "John", lastName = "Steinbeck"},Guest {firstName = "Henry", lastName = "Ford"},Guest {firstName = "Andrew", lastName = "Carnegie"},Guest {firstName = "Anton", lastName = "Chekhov"},Guest {firstName = "George", lastName = "Washington"},Guest {firstName = "William", lastName = "Shakespeare"},Guest {firstName = "Nathaniel", lastName = "Hawthorne"}]
"getGuest''"
[]

Я чувствую, что это должно быть правильное преобразование для выполнения, но мое понимание стрелок немного шаткое. Я делаю что-то неправильно? Это ошибка, о которой я должен сообщить?

Я использую HXT версии 9.3.1.3 (последняя на момент написания статьи). ghc --version печатает «Славная система компиляции Haskell Glasgow, версия 7.4.1». Я также тестировал на коробке с ghc 7.6.3 и получил тот же результат.

XML-файл имел следующую повторяющуюся структуру (полный файл можно найти в здесь)

<guestbook>
  <guest>
    <fname>John</fname>
    <lname>Steinbeck</lname>
  </guest>
  <guest>
    <fname>Henry</fname>
    <lname>Ford</lname>
  </guest>
  <guest>
    <fname>Andrew</fname>
    <lname>Carnegie</lname>
  </guest>
</guestbook>

person Gareth Charnock    schedule 24.02.2014    source источник
comment
Не могли бы вы опубликовать пример XML-файла для этого?   -  person bheklilr    schedule 24.02.2014
comment
@bheklilr Хорошо, сделал это.   -  person Gareth Charnock    schedule 24.02.2014


Ответы (2)


В getGuest'' у вас есть

... (hasName "fname") -< x
... (hasName "lname") -< x

То есть вы ограничиваетесь случаем, когда x равно "fname" и x равно "lname", что не удовлетворяет никакому x!

person Tom Ellis    schedule 24.02.2014
comment
Значит, недействительный факторинг? Я собираюсь прочитать документацию о том, как именно стрелочная нотация переводится в обычный Haskell. - person Gareth Charnock; 25.02.2014
comment
Ведь уж точно недействительно ни вообще, ни, оказывается, в данном конкретном случае. - person Tom Ellis; 25.02.2014
comment
Если вы хотите подумать о переводе, важным моментом является разница между f >>> (g1 &&& g2) и (f >>> g1) &&& (f >>> g2). - person Tom Ellis; 25.02.2014
comment
Теперь я вижу, что шланг отличается. Например, если у f был побочный эффект, то первый запускает ze ракет один раз, второй — дважды. Моя проблема заключается в том, чтобы мысленно перевести нотацию proc/do в базовые символы. Легко найти алгоритм перевода для нотации монады do и усвоить его, но единственный источник, который я нашел до сих пор, - это статья Росса Патерсона, которую действительно трудно понять. Каждое другое руководство, кажется, дает вам пример и перевод без объяснения того, как одно стало другим. - person Gareth Charnock; 25.02.2014

Мне удалось выяснить конкретную причину, по которой конструкция интерпретируется именно так. Следующий перевод стрелки, найденный здесь, служит основой для работы.

addA :: Arrow a => a b Int -> a b Int -> a b Int
addA f g = proc x -> do
                y <- f -< x
                z <- g -< x
                returnA -< y + z

Становится:

addA :: Arrow a => a b Int -> a b Int -> a b Int
addA f g = arr (\ x -> (x, x)) >>>
           first f >>> arr (\ (y, x) -> (x, y)) >>>
           first g >>> arr (\ (z, y) -> y + z)

Отсюда можно по аналогии вывести:

getGuest''' = preproc >>>
           arr (\ x -> (x, x)) >>>
           first f >>> arr (\ (y, x) -> (x, y)) >>>
           first g >>> arr (\ (z, y) -> Guest {firstName = z, lastName = y})

    where preproc = deep (isElem >>> hasName "guest") >>> getChildren
        f = getText <<< getChildren <<< (hasName "fname")
        g = getText <<< getChildren <<< (hasName "lname")

В HXT стрелки можно представить как потоки значений, протекающие через фильтры. arr (\x->(x,x)) не "разделяет поток", как я надеялся. Вместо этого он создает поток кортежей, которые фильтруются по f, а выжившие фильтруются по g. Поскольку f и g взаимоисключающие, выживших нет.

Примеры с getChildren внутри чудесным образом сработали, потому что поток кортежей содержал значения из более высокого XML-документа, выглядящие примерно так:

<guest>
    <fname>John</fname>
    <lname>Steinbeck</lname>
</guest>

и поэтому не были взаимоисключающими.

person Gareth Charnock    schedule 25.02.2014