Kurzeinführung in Haskell

Einführung in den Teil der funktionalen Programmiersprache Haskell, der für die Vorlesung Fixpunkte im Wintersemester 2002/03 von Interesse ist.

Aufruf

Entweder den Haskell-Interpreter hugs. Aufruf liefert
__   __ __  __  ____   ___      _________________________________________
||   || ||  || ||  || ||__      Hugs 98: Based on the Haskell 98 standard
||___|| ||__|| ||__||  __||     Copyright (c) 1994-2001
||---||         ___||           World Wide Web: http://haskell.org/hugs
||   ||                         Report bugs to: hugs-bugs@haskell.org
||   || Version: December 2001  _________________________________________

Haskell 98 mode: Restart with command line option -98 to enable extensions

Reading file "/soft/IFI/lang/hugs98-Dec2001/iX86-unknown-linux/share/hugs/lib/Prelude.hs":

Hugs session for:
/soft/IFI/lang/hugs98-Dec2001/iX86-unknown-linux/share/hugs/lib/Prelude.hs
Type :? for help
Prelude>
Enthält auch schon die WWW-Seite http://haskell.org/.
:v liefert die Version. Beenden mit :q. Generell kann man alle Befehle an Interpreter abkürzen (sofern unambig).

Oder den interaktiven Haskell-Compiler ghci:

   ___         ___ _
  / _ \ /\  /\/ __(_)
 / /_\// /_/ / /  | |      GHC Interactive, version 5.04, for Haskell 98.
/ /_\\/ __  / /___| |      http://www.haskell.org/ghc/
\____/\/ /_/\____/|_|      Type :? for help.

Loading package base ... linking ... done.
Loading package haskell98 ... linking ... done.
Prelude>
Unterschiede: ghci ist größer (enthält ja den ganzen Compiler), kann compilierte Module einbinden und erlaubt interaktive Definitionen, z.B. let zwei f x = f(f x);
Derartige Definitionen ließen sich mit hugs nur aus einer Datei laden.

Haskell-Programme

Befinden sich in Dateien, typischerweise mit Extension .hs
Es ist sinnvoll, denselben Namen wie für das darin befindliche Modul zu vergeben, also mit großgeschriebenem ersten Buchstaben.

Laden von Haskell-Dateien mittels ":l " und Datei-Grundnamen. Unser erstes Beispiel :l Zahlen.hs

Im Haskell-Programm: Kommentarzeilen beginnen mit --. Typ eines Ausdrucks bestimmen (in ghci, in hugs werden lediglich andere Namen für Typvariablen verwendet): :t zwei liefert forall t. (t -> t) -> t -> t

Beachte: Zeilenende ist Befehlsende (also kein Semikolon am Ende).

Damit wird also der Typ der im Programm definierten Funktion "zwei" von Haskell bestimmt. Es ist der allgemeinste Typ, der hier vergeben werden kann (im Rahmen des Hindley-Milner-Typsystems).

*Zahlen> :t null
forall t t1. t -> t1 -> t1
*Zahlen> :t eins
forall t1 t. (t1 -> t) -> t1 -> t
Diese beiden Typen sind allgemeiner als der Typ von zwei. Man liest forall t1 t als zwei Allquantoren.
*Zahlen> :t comp
forall t2 t1 t. (t2 -> t1) -> (t1 -> t) -> t2 -> t
Sehr naheliegender Typ.

*Zahlen> :t plus
forall t2 t t1 t3.
(t2 -> t -> t1) -> (t2 -> t1 -> t3) -> t2 -> t -> t3
Nun kann man das auch auf Terme spezielleren Typs anwenden:
*Zahlen> :t vier
forall t. (t -> t) -> t -> t
*Zahlen> :t fuenf
forall t. (t -> t) -> t -> t
*Zahlen> :t pl45
forall t. (t -> t) -> t -> t
Das Ergebnis läßt sich nicht ansehen, weil es eine Funktion ist:
*Zahlen> pl45

No instance for (Show ((t -> t) -> t -> t))
arising from use of `print' at 
In a 'do' expression pattern binding: print it
Wir behelfen uns zunächst wie folgt (+ ist für Num-Argumente vordefiniert.)
*Zahlen> :t inc
forall a. (Num a) => a -> a
Achtung: => meint eine Bedingung und keinen Funktionstyp! 0 ist auch vordefiniert als numerische Null.
*Zahlen> :t 0
forall t. (Num t) => t
*Zahlen> pl45 inc 0
9
Wer hier immer den Typ des Ergebnisses angezeigt bekommen möchte, kann hugs mit Option +t starten. Für ghci empfiehlt sich das Kommando :set +t. Danach erhält man z.B.
*Zahlen> pl45 inc 0
9
it :: Integer
*Zahlen> :t mal
forall t t1 t2. (t -> t1) -> (t1 -> t2) -> t -> t2
*Zahlen> :t ml45
forall t. (t -> t) -> t -> t
*Zahlen> ml45 inc 0
20
it :: Integer

Musterbasierte Definitionen

*Zahlen> :t fakultaet
forall a. (Num a, Ord a) => a -> a
*Zahlen> fakultaet 69
171122452428141311372468338881272839092270544893520369393648040923257279754140647424000000000000000
it :: Integer

Datentypen

Bislang hatten wir das Modul "Zahlen". Jetzt können wir das Modul "Datentypen" dazuladen mittels :a Datentypen (a für "also"). Mit :m Zahlen können wir wieder in das andere Modul zurückkehren.

*Datentypen> :t meinFarbPaar
(Farbe, Farbe)
*Datentypen> :t listeAllerFarben
[Farbe]
*Datentypen> [Rot,0]

:1:
    No instance for (Num Farbe)
    arising from the literal `0' at :1
    In the list element: 0
    In the definition of `it': [Rot, 0]
*Datentypen> meinZeichen
'r'
it :: Char
*Datentypen> meineBuchstabenListe
"ab"
it :: [Char]
*Datentypen> :t Wurzel
forall a. a -> BinTree a
*Datentypen> :t Verzweigung
forall a. (a, BinTree a, BinTree a) -> BinTree a
*Datentypen> :t Wurzel Rot
BinTree Farbe
Man kann aber solche Bäume nicht "ansehen":
*Datentypen> Wurzel 0

No instance for (Show (BinTree a))
arising from use of `print' at 
In a 'do' expression pattern binding: print it
(Achtung: Das geht nur dann schief, wenn Zeile 64 in Datentypen.hs auskommentiert ist.)
 *Datentypen> :t WurzelO
forall a. a -> BinTreeOhneProdukte a
*Datentypen> :t VerzweigungO
forall a.
a
-> BinTreeOhneProdukte a
   -> BinTreeOhneProdukte a -> BinTreeOhneProdukte a
Hier ist eine banale Ausgabefunktion gleich inbegriffen:
*Datentypen> WurzelO 0
WurzelO 0
it :: BinTreeOhneProdukte Integer
*Datentypen> meinBaumO
VerzweigungO 0 (WurzelO 1) (VerzweigungO 2 (WurzelO 3) (WurzelO 4))
it :: BinTreeOhneProdukte Integer
Das geht aber nicht immer:
*Datentypen> WurzelO Rot

No instance for (Show Farbe)
arising from use of `print' at 
In a 'do' expression pattern binding: print it
Dieser Fehler tritt auf, da wir bei der Definition von Farbe nicht ebenfalls deriving Show geschrieben hatten.
*Datentypen> :t showProviB
forall a. (Show a) => BinTree a -> String
*Datentypen> :t showB
forall a. (Show a) => BinTree a -> String
*Datentypen> showProviB meinBaum
"<0|1|<2|3|4>>"
it :: String
*Datentypen> showB meinBaum
"<0|1|<2|3|4>>"
it :: String
Nach dem Entfernen des Kommentars um die "magische Beschwörung":
*Datentypen> :l Datentypen
Compiling Datentypen       ( Datentypen.hs, interpreted )
Ok, modules loaded: Datentypen.
*Datentypen> meinBaum
<0|1|<2|3|4>>
it :: BinTree Integer

Rang-2-Datentypen

Wir verwenden weiter Datentypen.hs mit dem Modul Datentypen.
*Datentypen> :t summeB
forall a. (Num a) => BinTree a -> a
*Datentypen> summeB meinBaum
10
it :: Integer
*Datentypen> :t listifyB
forall a. BinTree a -> [a]
*Datentypen> listifyB meinBaum
[0,1,2,3,4]
it :: [Integer]
*Datentypen> :t Zero
forall a. a -> PList a
*Datentypen> :t Succ
forall a. PList (a, a) -> PList a
*Datentypen> :t meinePowerListe
PList Integer
*Datentypen> :t summeErweitertP
forall a. (a -> Integer) -> PList a -> Integer
*Datentypen> :t summeP
PList Integer -> Integer
*Datentypen> summeP meinePowerListe
36
it :: Integer
*Datentypen> :t listifyP
forall a. PList a -> [a]
*Datentypen> listifyP meinePowerListe
[1,2,3,4,5,6,7,8]
it :: [Integer]

Ralph Matthes
Last modified: Tue Oct 29 22:44:37 CET 2002