2013-08-16 33 views
5

Pozwala zobaczyć fragment kodu:Nie można obliczyć minimalną długość parsera - uu-parsinglib w Haskell

pSegmentBegin p i = pIndentExact i *> ((:) <$> p i <*> ((pEOL *> pSegment p i) <|> pure [])) 

jeśli zmienię ten kod w moim parsera do:

pSegmentBegin p i = do 
    pIndentExact i 
    ((:) <$> p i <*> ((pEOL *> pSegment p i) <|> pure [])) 

mam błąd:

canot compute minmal length of a parser due to occurrence of a moadic bind, use addLength to override 

Pomyślałem, że powyższy parser powinien zachowywać się w ten sam sposób. Dlaczego ten błąd może wystąpić?

EDIT

Powyższy przykład jest bardzo proste (uproszczenie pytanie) i jak wskazano poniżej niej nie jest konieczne stosowanie zrobić notacji tutaj, ale prawdziwy przypadek chciałem go użyć jest następująco:

pSegmentBegin p i = do 
    j <- pIndentAtLast i 
    (:) <$> p j <*> ((pEOL *> pSegments p j) <|> pure []) 

zauważyłem, że dodanie „addLength 1” przed zrobić oświadczenie rozwiązuje problem, ale jestem pewien, jeśli jego poprawne rozwiązanie:

pSegmentBegin p i = addLength 2 $ do 
    j <- pIndentAtLast i 
    (:) <$> p j <*> ((pEOL *> pSegments p j) <|> pure []) 

Odpowiedz

7

Jak już wspomniałem wiele razy, monadyczny interfejs powinien być unikany, kiedy tylko jest to możliwe. pozwól mi spróbować wyjaśnić, dlaczego preferowany jest interfejs aplikacyjny.

Jedną z wyróżniających cech mojej biblioteki jest to, że wykonuje korekcję błędów, wstawiając lub usuwając problemy. Oczywiście możemy przyjąć tutaj nieograniczone spojrzenie, ale dzięki temu proces BARDZO drogi. Tak więc ograniczamy się tylko do trzech kroków.

Załóżmy teraz musimy wstawić ekspresję i jedną z alternatyw jest wyrażenie:

wyrażenie: = „if” wyrażenie „a następnie” wyrażenie „else” wyrażenie

następnie chcemy wykluczyć tę alternatywę ponieważ wybór tej alternatywy wymagałby wstawienia innego wyrażenia itp. Przeprowadzamy zatem abstrakcyjną interpretację alternatyw i upewniamy się, że w przypadku remisu (tj. równe koszty ograniczonego wyprzedzenia) bierzemy jedną z nierekurencyjnych alternatyw.

Niestety ten schemat ulega awarii, gdy pisze się monadyczne analizatory składni, ponieważ długość prawej strony wiązania może zależeć od wyniku po lewej stronie.Wydajemy więc komunikat o błędzie i prosimy programistę o pomoc w wskazaniu liczby tokenów, które ta alternatywa może zużyć. Rzeczywista wartość nie ma większego znaczenia, o ile nie zapewnisz skończonej długości dla czegoś, co jest rekurencyjne i może prowadzić do nieskończonych wstawień. Jest używany tylko do wyboru najkrótszej alternatywy w przypadku wstawienia.

Ta abstrakcyjna interpretacja wiąże się z pewnymi kosztami i jeśli napiszesz wszystkie swoje parsery w monadycznym stylu, nieuniknione jest, że ta analiza powtarza się od nowa. więc: NIE NALEŻY NAPISOWAĆ MONADYCZNYCH STYLÓW STYLU PRZY KORZYSTANIU Z TEJ BIBLIOTEKI, JEŻELI JEST APLIKACJĄ ALTERNATYWNĄ.

+0

Dziękuję! Bardzo dużo wyjaśnia. Myślę, że w moim przypadku muszę użyć kombinatora parsera stylu monadycznego ('pSegmentBegin' zużywa spacje, policz nowy poziom wcięcia, a następnie wymusza wszystkie linie poniżej, aby użyć tego wcięcia), więc nie jest możliwe zapisanie go w" czystej aplikacji " "style –

+1

Zgodnie z twoim pytaniem o' StackOverflow' - nie wiem, czy jest jakakolwiek subskrypcja pytań dostępna - jeśli nie korzystałem z niej do tej pory, ale myślę, że zamieszczanie tutaj pytań jest bardzo dobrym pomysłem - kiedy szukałem cokolwiek na temat 'uu-parsinglib' - jedyne znalezione wyniki znajdowały się na' StackOverflow' i dodatkowo wielu programistów go uderzyło - jest o wiele bardziej dostępny niż jakakolwiek lista mailingowa. –

+0

Witam @Doaitse! Możesz otrzymywać powiadomienia e-mail o wszystkich przyszłych pytaniach SO, które zawierają tag [tag: uu-parsinglib], umieszczając wskaźnik myszy nad tagiem i klikając "Subskrybuj". Zmienię twoje pytanie, by wyczyścić nieistotne punkty i wyostrzyć cios :) Wszystko co najlepsze. – ulidtko

4

Próbuje statycznie przeanalizować, ile danych wejściowych należy odczytać, aby zoptymalizować wydajność, ale tego rodzaju optymalizacja wymaga statycznie znanej struktury parsera - takiej, która może być zbudowana przez Applicative s, ponieważ efekt parsera nie może zależeć od parsera wartość taka, jaką robi (>>=).

A więc tak się dzieje - podczas korzystania z zapisu do przekłada się na wiązanie Monadic, które przerywa predykcję Applicative. Byłoby miło, gdyby biblioteka ujawniła tylko jeden z dwóch interfejsów, tak aby tego rodzaju błąd nie mógł się zdarzyć, ale zamiast tego istnieje pewna niespójność, jeśli używasz obu interfejsów razem w tym samym parserze.

Ponieważ korzystanie z do jest absolutnie niepotrzebne - nie korzystasz z dodatkowej mocy, którą zapewnia interfejs monadyczny - prawdopodobnie lepiej po prostu tego uniknąć.

+1

Czy nie '(>>)' i '(*>)' ma być odpowiednikiem? –

+1

Istnieje niepisana reguła, że ​​instancje 'Applicative' i' Monad' powinny się zgadzać, w przeciwnym razie rzeczy się mylą (jak tutaj), ale jeśli używasz właściwości 'Applicative', które możesz uzyskać w' Monad', to możesz pisać instancja 'Applicative', która nie może mieć' Monad'. –

+0

Stanie się pisemną regułą [gdy każde 'Monad' stanie się' Applicative'] (http://www.reddit.com/r/haskell/comments/1k3fq7/what_are_some_killer_libraries_and_frameworks/cbl4d8r). I możesz * udowodnić *, że domyślne wiązania dla '(>>)' i '(*>)' są równoważne, używając tylko praw monady i założeniu, że przy założeniu, że 'ap = (<*>)'. –