[ Seminarthemen SS 2001] ... [ Inhaltsverzeichnis ] ... [ Fallbeispiel Damenproblem]
(1) #! /usr/bin/runhugs (2) main :: IO() (3) main = putStr ("Content-type: text/html\n\n" ++ "<html>" ++ "<head><title>HelloWorld</title></head>" ++ "<body><h1>Hello World</h1></body>" ++ "</html>")Das Skript wird vom Kommandozeilen-Interpreter runhugs ausgeführt und seine Ausgabe als HTTP-Response an den Client zurückgeschickt.
Diese Art der Generierung von HTML Seiten ist sehr mühsam und auch fehleranfällig. Aus diesem Grund haben mehrere Autoren HTML-Bibliotheken entwickelt, die dem Programmierer Funktionen bieten mit deren Hilfe er sehr einfach valide Dokumente erzeugen kann. Die im folgenden näher betrachtete cgi-Bibliothek von Erik Meijer bietet dem Benutzer darüber hinaus eine Schnittstelle mit deren Hilfe er von der gesamten Low-Level Kommunikation via HTTP abstrahieren kann. Dies hat den Vorteil, daß sehr viel mehr Zeit in die Lösung des eigentlichen Problems gesteckt werden kann.
Um die vielen Details des HTTP für den Entwickler der Skripte transparent zu halten, besteht die Bibliothek aus zwei Komponenten. Einem Wrapper und einem Worker. Die Aufgaben, die bei jedem cgi-Skript gleich sind, werden vom Wrapper übernommen. Die eigentlich Programmlogik wird im Worker gekapselt.
Durch diesen Ansatz ist es möglich, kürzere und besser wartbare Programme zu schreiben.
Der Ablauf im Wrapper stellt sich in etwa wie folgt dar.
wrapper :: (Request -> IO Response) -> IO () (1) wrapper = \worker -> do { (2) request <- getRequest (3) ; response <- worker request (4) ; putResponse response } 'catch' (5) (\ioerror -> do {putResponse(status ioerror)})Die Definition des Wrapper ist ein Beispiel für die Nutzung von Higher Order Functions. Der Funktion wrapper wird eine Funktion mit der Signatur
übergeben, die in (1) als worker benannt und in (3) aufgerufen
wird. Vor dem Aufruf des Workers wird der low-level Request in einen abstrakten
Request vom Typ Request umgeformt. Dieser Request wird dem Worker als
Parameter mitgegeben. Das Ergebnis des Workers (ein Wert vom Typ Response)
wird in (4) in einen low-level Response umgewandelt und dem Client zurückgeschickt.
Im Falle eines Fehlers wird der Fehlercode automatisch an den Client übermittelt.
Ich verzichte in dieser Ausarbeitung bewußt auf die Details zur Implementierung dieser Umformungen, da sie - wie bereits mehrfach erwähnt - für die Benutzung der Bibliothek keine Rolle spielen. Der interessierte Leser sei auf die exzellente Dokumentation von Meijer verwiesen.
beschrieben. Häufig lassen sich die Elemente durch Einfügen optionaler Attribute
der Form
parametrisieren. Die hier vorgestellte Bibliothek definiert
einen Datentyp HTML als:
data HTML = Text String | Element Tag [(Name, Value)] [HTML]
Die Typen Tag, Name und Value sind Synonyme für den Datentyp String (vergleiche Abschnitt 3.2). Somit ist ein Wert des Typs HTML entweder einfacher Text oder ein Element. Ein Element besteht aus einem Tagnamen, beliebig vielen Attributen und beliebig vielen weiteren Elementen. Um diese Elemente nicht per Hand erzeugen zu müssen stehen selbstverständlich Funktionen bereit, die diese Aufgabe übernehmen. Folgenden Übersicht zeigt die wichtigsten Funktionen . Unter Zuhilfenahme dieser Funktionen lassen sich sehr einfach selbst komplexe Seiten erzeugen.
page :: String -> [(Name,Value)] -> [HTML] -> HTML h1 :: String -> HTML h :: Int ->String -> HTML p :: [HTML] -> HTML hr :: HTML href :: URL -> [HTML] -> HTML img :: String -> URL -> HTML ol :: [[HTML]] -> HTML ul :: [[HTML]] -> HTML dl :: [(String,[HTML])] -> HTML table :: String -> [String] -> [[[HTML]]] -> HTML
import CGI hello :: HTML hello = page "Hello World" [("bgcolor", "#000000")] [h1 "Hello World"] main :: IO () main = wrapper (\env -> do return (Content(hello)))Bei diesem zugegebenermaßen sehr einfachen Beispiel ist der Schreibaufwand nicht viel geringer als bei der ersten Version, aber das Programm gewinnt stark an Lesbarkeit und Wartbarkeit. Je umfangreicher und komplexer die zu erstellende Seite ist, desto höher sind diese Vorteile einzuschätzen.
auch indiziert auf eine spezielle Variable zugreifen.
seite :: [(Name, Value)] -> HTML seite env = page "Umgebungsvariablen" [] [ h1 "Umgebungsvariablen", dl (map (\(dt,dd) -> (dt, [prose dd])) env) ] main :: IO () main = wrapper(\env -> do return (Content(seite env)))
[ Seminarthemen SS 2001] ... [ Inhaltsverzeichnis ] ... [ Fallbeispiel Damenproblem]