[Haskell] Verständnisproblem

Skid

Erfahrenes Mitglied
Hallo zusammen,

ich habe in der Haskell-Wiki gekramt, da ich derzeit im Rahmen meines Studiums eine Ausarbeitung anfertigen muss zum Thema Iteratee IO. Ich bin über die Wiki auf folgende Funktionen gestoßen:

Code:
enumerator :: FilePath -> Iteratee (Maybe Char) o -> IO o
enumerator file it = withFile file ReadMode
  $ \h -> fix (\rc it -> case it of
    Done o -> return o
    Next f -> do
      eof <- hIsEOF h
      case eof of
        False -> do
          c <- hGetChar h
          rc (f (Just c))
        True -> rc (f Nothing)
    ) it

Ich verstehe den Kern der Funktion. Das heißt, dass bei Done, der Iteratee mit dem Berarbeiten fertig ist und den Inhalt der Datei ausgibt. Bei Next wird nacheinander geprüft, ob sich die Datei am Ende der Datei befindet. Im Fall 'False' wird weiterhin ein Zeichen aus der Datei gelesen, im Falle von 'True' Nothing zurückgegeben, wohin der Iteratee das Signal bekommt, dass die Datei erschöpft ist, bzw. am Ende ist. Was mir jetzt nicht ganz kalr ist, ist dieses 'rc', die ersten drei Zeilen und das 'it' hinter der Klammer. Der Ausdruck in der Klammer selbst ist ja eine Lambda Funktion, mit dem Parameter 'rc', oder ist das schon falsch?

Ich hoffe ihr könnt mir da weiterhelfen. Ich verstehe zwar so einiges in Haskell, aber da ich relativ neu bin, sind solche komplizierte Funktionen für mich eher undurchsichtig.

Beste Grüße,
SKiD.
 
Hi.
Skid hat gesagt.:
Was mir jetzt nicht ganz kalr ist, ist dieses 'rc'
rc ist die Rekursionsfunktion deren Fixpunkt berechnet wird.
Skid hat gesagt.:
die ersten drei Zeilen
Was ist denn an den Zeilen unklar?
Skid hat gesagt.:
und das 'it' hinter der Klammer.
Das it ist nur der Parameter welcher dem Resultat der fix Funktion (was wieder eine Funktion ist) übergeben wird.
Skid hat gesagt.:
Der Ausdruck in der Klammer selbst ist ja eine Lambda Funktion, mit dem Parameter 'rc', oder ist das schon falsch?
Nicht ganz richtig. Die Lambda Funktion besitzt 2 Parameter rc und it.

Gruß
 
Hallo deepthroat,

danke für deine Antwort. Das hilft mir schon mal weiter. Zu den ersten drei Zeilen:
Ich habe da nochmal etwas nachgeblättert und weiß nun, dass die erste Zeile im Grunde ja die Typen für Parameter und Rückgabetyp angibt. Das wird hier denke ich auch nicht anders sein, jedenfalls kenne ich das so von einfachen Funktionen aus Tutorials usw.

Die Zeile 2 ist also demnach die Funktionsdefinition mit Parametern. Nachdem Gleichheitszeichen wird die Datei aufgerufen, aus der gelesen werden soll, auch soweit verständlich. In Zeile 3 wird eine Lambda-Funktion definiert mit dem Parameter "h" in der die fix-Funktion mit einer weiteren Lambda-Funktion (die wir schon besprochen haben) aufgerufen wird. Ist das soweit richtig?

Nicht ganz richtig. Die Lambda Funktion besitzt 2 Parameter rc und it.

Okay ich hatte das "rc" übersehen. Danke. :)

Zwei Anliegen habe ich noch: Es gibt noch zwei weitere Funktionen aus der Wiki. Eine die zwei Iteratoren sequentiell nacheinander (siehe Funktion 1) schaltet und eine, die zwei Iteratees parallel laufen lässt (siehe Funktion 2). Allerdings fehlen dort jegliche Beschreibungen, was die Funktion eigentlich macht und für was diese gebraucht wird. Lediglich existiert dort nur die Erklärung, wie die Iteratees nacheinander bzw. parallel geschaltet werden.

Funktion 1:
Code:
{- s = state -}
instance Functor (Iteratee input) where
  fmap f = fix $ \rc s -> case s of
    Done o -> Done (f o)
    Next g -> Next (rc . g)
instance Monad (Iteratee input) where
  return = Done
  it0 >>= it1 = fix (\rc s -> case s of
    Done o -> it1 o
    Next g -> Next (rc . g)
    ) it0
Funktion 2:
Code:
arr0 f = Next $ \i -> Done (f i)
instance Category Iteratee where
  id = arr0 id
  it1 . it0 = fix (\rc1 it1 -> case it1 of
    Done c -> Done c
    Next f1 -> fix (\rc0 it0 -> case it0 of
      Done b -> rc1 (f1 b)
      Next f0 -> Next (rc0 . f0)
      ) it0
    ) it1
Bei der letzten Funktion, schätze ich, soll eine ID kategorisiert werden?
Das ist für mich schon etwas zu kryptisch...

Vielleicht hat jemand von euch eine Idee?

Beste Grüße und danke nochmal für die Antwort,
SKiD.

//Edit:
Ich habe mich glaube ich vertan. Die erste Funktion ist der Monoid für "Iteratee i". Dort wird eben die Konstante "return" definiert, die Done zurückgibt. Ansonsten der Bindung-Operator >>= mit den Fällen von "s" mit "Done o" für die Rückgabe von "o" an "it1" und dem Fall "Next g" für den Aufruf (rc . g). Ich kann es leider nicht so gut erklären...
Was das jetzt allerdings mit dem Functor alles zu tun hat, weiß ich leider nicht. Dort wird ein f and fmap übergeben und eine Lambda-Funktion aufgerufen, die wiederrum "Next" und "Done" als Fall enthält für "s". Was ich da nicht ganz verstehe ist, was "rc" genau ist. Ich weiß, dass das ein Parameter ist, aber kann mir darunter speziell nichts vorstellen. Genauso bei "o".
 
Zuletzt bearbeitet:
Hi.
Ist das soweit richtig?
Ja.
Zwei Anliegen habe ich noch: Es gibt noch zwei weitere Funktionen aus der Wiki. Eine die zwei Iteratoren sequentiell nacheinander (siehe Funktion 1) schaltet und eine, die zwei Iteratees parallel laufen lässt (siehe Funktion 2).
Dabei handelt es sich nicht um Funktionen, sondern um zwei Instanziierungen der Typklasse Monad und der Typklasse Functor in Bezug auf den Typ "Iteratee input".

Ein Iteratee ist also ein Functor, da für diesen Typ eine Funktion fmap (laut der Typklasse) definiert ist.

Ein Iteratee ist auch eine Monad, da für diesen Typ die Funktion return und der Sequenzoperator definiert ist.

Siehe z.B. http://www.haskell.org/tutorial/classes.html
Was ich da nicht ganz verstehe ist, was "rc" genau ist. Ich weiß, dass das ein Parameter ist, aber kann mir darunter speziell nichts vorstellen.
Dazu müßtest du die fix Funktion etwas näher betrachten. "rc" ist im Grunde die Rekursions-Fall-Unterscheidung (recursion-case).

Und 'o' ist doch nur der Wert aus dem Pattern match. Oder welches 'o' meinst du?

Gruß
 
Dabei handelt es sich nicht um Funktionen, sondern um zwei Instanziierungen der Typklasse Monad und der Typklasse Functor in Bezug auf den Typ "Iteratee input".

Okay, ich glaube ich muss mir das mit dem Monad und Functor nochmal genauer anschauen. Ich verstehe das zwar, z.B. auch was in "Learn You A Haskell" steht, aber wenn ich dann einen Codeschnippsel vor mir habe, setzt es irgendwie aus. Danke ich werde mir deinen Link und die Kapitel aus dem genannten Buch nochmal zur Gemüte führen!

Dazu müßtest du die fix Funktion etwas näher betrachten. "rc" ist im Grunde die Rekursions-Fall-Unterscheidung (recursion-case).

Und 'o' ist doch nur der Wert aus dem Pattern match. Oder welches 'o' meinst du?

Also da stand ich jetzt wahrscheinlich gehörig auf dem Schlauch. Im Grunde ist es also ein rekursiver Aufruf wie "fix f -> f (fix f)" eben nur mit "rc" (was ja eigentlich schon ein Wink mit dem Zaunpfahl ist).

Wenn ich mir diesen Codeteil anschaue:
Code:
it0 >>= it1 = fix (\rc s -> case s of
    Done o -> it1 o
    Next g -> Next (rc . g)
    ) it0

Wird ein Patternmatching für "s" gemacht. Wenn ein "s" = "Done o" ist, wird die "it1 0" aufgerufen. Ist das nicht der Fall, also "Next g", wird "Next(\x -> rc(g x))" (kurz: "Next (rc . g)") ausgeführt. Also theoretisch rekursiv weiter in die Tiefe gegangen, mit dem einem Wert, der von "g" aufgerufen wird und anschließend von der rekursiven Funktion "rc". Ist "it1" fertig, wird "it0" ausgeführt.

Also so würde ich es jetzt verstehen. Vorhin schaute ich noch die Sau ins Uhrwerk. ;)

Beste Grüße,
SKiD.
 
Jep, da bin ich wohl auf die falsche Taste gekommen ~.~
Okay, ich glaube jetzt habe ich es verstanden. Ich werde mir das mit dem Monoids und Functoren nochmal anschauen. Das empfinde ich nicht ganz als einleuchtend, warum ich "so etwas" brauche. ;)

Danke dir für deine Hilfe. :)
 
Zurück