Praktische Informatik 3 WS 2007/08 Bonusblatt Ausgabe: 17. 12. 2007 Abgabe bis 28. 1. 2008 Haskell conquers the continent Berthold Hoffmann <hof> Klaus Hartke <hartke> Christian Maeder <maeder> Dennis Walter <dw> Diedrich Wolter <dwolter> 20 Punkte Die Sprache Haskell ist in der Wahl ihre Schlüsselwörter auf den angelsächsischen Markt zugeschnitten. Diese sträfliche Vernachlässigung des “continents”, also des guten alten Festlandeuropas mit seinem schier babylonischen Sprachengewirr soll behoben werden, indem nunmehr auch nationale Dialekte von Haskell in allen Sprachen der europäischen Gemeinschaft auf den Markt gebracht werden sollen. Das wohlbekannte if-then-else könnte beispielsweise in der deutschen Fassung wenn-dann-sonst und in der französischen si-alors-sinon heißen. Damit soll die marktbeherrschende Rolle von Haskell bei den verzögert ausgewerteten funktionalen Sprachen in allen EU-Ländern ausgebaut werden. Die Umstellung der Haskell-Systeme wie hugs oder ghc selbst sollte eigentlich ein Kinderspiel sein, weil sie ja sicher modular implementiert sind, so dass nur die konkreten Zeichenketten für die Schlüsselwörter ausgetauscht werden müssen. Was aber wird aus den unzähligen Terabyte an existierenden Haskell-Programmen? Auch die sollten in die nationalen Dialekte von Haskell übersetzt werden können, damit sie danach besser weiter entwickelt werden können. Hier kommen Sie ins Spiel! Schreiben Sie ein Haskell-Programm (im ursprünglichen, englischen Dialekt), dessen Hauptfunktion folgende Signatur hat: type Text = [String] translate :: [(String,String)] -> Text -> Text Das erste Argument enthält alle Paare (e, d) von Schlüsselwörtern, die übersetzt werden sollen. Jedes Auftreten eines Schlüsselworts e im zweiten Argument, (dem Originalprogramm) soll im Ergebnis (dem Zielprogramm) durch das entsprechende Wort d ersetzt werden. Dabei sind einige Besonderheiten zu beachten: • Nur Folgen von Buchstaben mit eventuell eingestreuten Unterstrichen können Schlüsselwörter sein. • Es kommen nur maximale Folgen in Frage, d. h., die Folgen müssen von anderen Zeichen eingeschlossen sein. • Alle Bezeichner des Quellprogramms, die im Zielprogramm als Schlüsselwörter gelten würden, werden dem Übersetzer für den Zieldialektes (bzw. eigentlich: dessen Benutzern) Probleme bereiten, weil dies zu Syntaxfehlern führen. Solche Namenskonflikte (name clashes) können nicht zuverlässig automatisch behoben werden. Sie könnten aber diesen Bezeichnern die Ziffer 1 anhängen und danach noch eine Warnung einfügen, z.B. in Gestalt eines Kommentars “{- Schlüsselwort <d> verändert zu <d>1! -}”. Die Signatur von translate erlaubt es, beliebige Wörterlisten für beliebige Sprachen anzugeben und somit zwischen beliebigen Haskell-Dialekten zu übersetzen, beispielsweise portugiesisches in ungarisches Haskell. Außerdem könnten Sie (oder die von Ihnen gegründete Firma, später) es nicht bei der Übersetzung von Schlüsselwörtern belassen, sondern auch die Namen der Typen und Funktionen im Prelude in die Zielsprache zu übersetzen. Erstellen Sie das Programm in folgenden Schritten: 1. Implementieren Sie einen Modul Dictionary, der einen abstrakten Datentyp Dictionary a zur effizienten Darstellung des Wörterbuchs implementiert. Ein Wörterbuch ist eine Liste von sogenannten Tries, n-stelligen Bäumen, die alle Wörterbucheinträge für den gleichen Anfangsbuchstaben enthalten, deren Knoten die folgenden Komponenten enthalten: (a) Der Anfangsbuchstaben aller Wörter in diesem Knoten und seinen Unterbäumen. (b) Falls dieser Anfangsbuchstabe der letzte eines Wortes sein kann, dessen Übersetzung (ein Wert vom Typ a), sonst nichts. (Dies kann mit Maybe a implementiert werden.) (c) Eine Liste von Unterbäumen, für alle mögliche Fortsetzungen der Wörter in diesem Baum. Auf dieser Datenstruktur werden Sie folgende Operationen brauchen: (a) empty:: Dictionary a ist das leere Wörterbuch. (b) plus:: (String, a) -> Dictionary a -> Dictionary a trägt ein Wort mit seiner Übersetzung in ein Wörterbuch ein. (c) find:: String -> Dictionary a -> Maybe a liefert die Übersetzung einer Zeichenkette, wenn sie ein Schlüsselwort ist, und nichts sonst. (d) fill :: [(String,a] -> Dictionary a füllt das Wörterbuch mit einer Liste von Paaren. 2. Die Funktion translate soll die Wörterliste in das Wörterbuch eintragen und dann den Programmtext übersetzen. Dazu sollte der Text in Bezeichner und Zwischentext aufgespalten werden, alle als Schlüsselwort erkannten Bezeichner übersetzt werden, und danach alles wieder zu einem Text zusammengebaut werden. Für alle Bezeichner, die in der Zielsprache zu Schlüsselwörtern werden, sollen wie oben diskutiert verändert werden; Dazu brauchen Sie ein “umgekehrtes” Wörterbuch für die Namenskonflikte der Zielsprache, das aber aus dem ersten leicht berechnet werden kann. (Die Einträge in diesem Wörterbuch haben den Typ (), d.h., die Funktion find liefert entweder Just (), wenn der Bezeichner ein Schlüsselwort der Zielsprache ist, oder Nothing andernfalls. 3. Definieren Sie mindestens ein Wörterbuch, das mindestens die in Tabelle 1 aufgeführten Haskell-Schlüsselwörter nach Deutsch oder nach Französisch übersetzt. Vorschläge für eine bessere Übersetzung der Schlüsselwörter, Erweiterungen der Wörterbücher oder Wörterbücher für weitere europäische Sprachen (estnisch, slowenisch) sind willkommen! (Vom Einstieg in den asiatischen Markt – Chinesisch, Japanisch, Indisch – raten wir jedoch ab, weil das Arbeiten mit Unicode-Zeichen einige zusätzliche Komplikationen mit sich bringt.) 4. Erweitern Sie dass Programm so, dass es als Kommando translate Programm.hs Programm.hsd aufgerufen werden kann und die “hs”-Datei in eine “hsd”-Datei übersetzt. Ein guter Testfall für Ihr Programm ist Ihr Programm selbst; Übersetzerbauer nennen das auf gut Deutsch einen “halben bootstrap”. Noch besser übersetzen Sie erst von “hs” nach “hsd” und dann – mit den inversen Wörterbüchern – zurück und überzeugen sich, ob dabei wieder das Originalprogramm herauskommt. Dieses ist Version 0.9 vom 11. Dezember 2007. Englisch as case class data default deriving do else hiding if import in infix infixl infixr instance let module newtype of otherwise qualified then type where Deutsch als falls Klasse Datentyp normalerweise ableitend tue sonst verdeckend wenn importiere in infix infixL infixR Instanz sei Modul Neuer Typ von andernfalls qualifiziert dann Typ wobei Französisch comme en cas classe donnee normalement derivant faites sinon cachant si importez en infixe infixeG infixeD instance soi module nouveau type de autrement qualifie alors type ou Ihre Lieblingssprache Tabelle 1: Schlüsselwörter von Haskell in Deutsch und Französisch (und Ihre Lieblingssprache)