Hey,
ich "muss" in der Uni in einem Kurs mit Haskell arbeiten. Nun, funktional ist glaube ich nicht ganz meine Welt, wobei ich mich mittlerweile immer besser in die Denkensweise einarbeiten kann. hauptsächlich lerne ich mit dem Buch "Programming in Haskell" von Graham Hutton, welches auf dem Gebiet ja zu den Standardwerken zählt. Ich versuche mich grad in Parser einzuarbeiten, da dass das nächste Thema ist, was ansteht. Dazu wird im Buch erst folgendes erstellt:
|
Quellcode
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
type Parser a = String -> [(a,String)]
return :: a -> Parser a
return v = \inp -> [(v,inp)]
failure :: Parser afailure = \inp -> []
item :: Parser Char
item = \inp -> case inp of
[] -> []
(x:xs) -> [(x,xs)]
parse :: Parser a -> String -> [(a, String)]
parse p inp = p inp
(>>=) :: Parser a -> (a -> Parser b) -> Parser b
p >>= f = \inp -> case parse p inp of
[] -> []
[(v,out)] -> parse (f v) out
|
Es wird also ein Typ für Parser a festgelegt, welcher eine Liste mit Tupeln von a und Strings ausgibt. Gedacht ist es so, dass wenn ein Parser fehlschlägt, die leere Liste zurück gegeben wird. Im Normalfall, enthält die Liste ein Element, und für den Fall, dass der String auf verschiedene Möglichkeiten verarbeitet werden kann, kann man nun die verschiedenen Varianten in der Liste ausgeben. Soweit so gut.
Es werden dann ein paar Standardparser erstellt.
return schlägt nie fehl, und gibt den Parameter als result zurück. Der übergebene String (Siehe Lambda) wird im Tupel an 2ter Stelle zurückgegeben. Diese 2te Stelle ist dafür gedacht, einen Reststring zurück zu geben, falls nicht der ganze String vom Parser verarbeitet werden kann.
failure schlägt immer fehl und gibt die leere Liste zurück.
item schlägt mit leeren Strings fehl, gibt ansonsten das erste Zeichen als result zurück.
Die Funktion parse ist einfach nur als Hilfsfunktion gedacht, damit man die einzelnen Parser "einfacher" testen kann. Quasi sowas wie parse (return 1) "abc" was dann [(1, "abc")] zurück geben sollte.
Bis hier hin ist alles soweit klar, wobei er sich bei mir beschwert, wenn ich return aufrufen will, da return ja schon definiert ist. Finde ich schon merkwürdig dass es dann so in das Buch aufgenommen wird, aber zum testen habe ich die Funktion dann einfach in preturn umbenannt, für parser-return. Dann klappts auch soweit.
>>= ist als Operator für sequencing gedacht, damit man die einzelnen Parser hintereinander schalten kann. Hier wird wirds jetzt komisch.
Es wird ein Testparser erstellt:
|
Quellcode
|
1
2
3
4
5
|
p :: Parser (Char,Char)
p = do x <- item
item
y <- item
return (x,y)
|
Das Prinzip dahinter ist mir soweit klar. Trotzdem finde ich es merkwürdig, dass ein Operator für sequencing erstellt wird und dann nicht genutzt wird. Wäre ja soweit kein Problem, doch er meckert.
Die Fehlermeldung:
No instance for (Monad ((-)) String))
arising from a use of 'return'
Als mögliche Korrektur wird mir vorgeschlagen, eine Deklaration für (Monat ((-)) String)) anzugeben.
Von Monaden stand bis jetzt noch nichts im Buch. Deshalb gehe ich davon aus, dass man hier für keine Monaden braucht. Sehe ich das richtig? Monaden werden weiter hinten im Buch zwar behandelt, aber es macht ja keinen Sinn, da in den Codestücken keine Monaden definiert wurden.
Nun geht das überhaupt ohne Monaden? Soweit ich weiß sind diese ja dafür da Funktionen nacheinander aufzurufen, von daher wäre das hier ja eigentlich ein Einsatzgebiet dafür. In der Vorlesung sind Parser jedoch auch vor Monaden geplant, von daher denke ich, dass es auch ohne gehen müsste. Kennt jemand eine Lösung für das Problem, oder geht das wirklich nur mit Monaden?