Haskell

**Haskell** on standardiseeritud üldotstarbeline puhtalt funktsionaalne programmeerimiskeel. Keel on nimetatud loogiku Haskell Curry järgi.

Haskell toetab laiska väärtustamist, mustrisobitust, loendite hõlmamist, tüübiklasse ja tüüpide polümorfismi. Kuna tegemist on puhta funktsionaalse keelega, siis puuduvad Haskelli funktsioonidel kõrvalefektid. Viimaste esitamiseks on eraldi andmetüüp: monaad.

## Süntaksist üldiselt

Kommentaari algust tähistavad Haskellis kaks sidekriipsu (**--**). Näiteks
 -– Siin on kommentaar
Kommentaar võib olla eraldi real või rea lõpus.

Sarnaselt Pythonile eraldatakse Haskellis koodiplokke läbi taandamise. Näiteks funktsiooni keha ei tohi olla funktsiooni nimega samal kaugusel ekraani vasakust servast. Kui taanded pole korralikult vormistatud, siis kuvatakse vastavasisuline veateade ning programmi ei saa käivitada.

## Laisk ja agar väärtustamine
Haskellis väärtustatakse kõik avaldised laisalt, mis tähendab, et avaldised väärtustatakse ainult siis, kui nende väärtus on tingimata käesoleval arvutuse sammul oluline. Kui aga avaldise väärtus pole hetkel oluline, siis jäetakse väärtustamata avaldis mällu ja vajadusel väärtustatakse hiljem. Haskellis on olemas ka operaator **$!** (dollarimärk ja hüüumärk), mis sunnib agaralt väärtustama. Näiteks deklaratsioonis
 eelnevateSumma n
   = ($!) eelnevateSumma (n – 1) * n
operaator **$!** sunnib väärtustama avaldist **n – 1**.

## Hello world

Järgneb Haskellis kirjutatud programm "Hello world" (kõik read peale viimase võib ära jätta):

```
module Main where
 
main :: IO ()
main = putStrLn "Hello world!"
```


`module Main where` on programmi päis. See sisaldab võtmesõna `module`, protseduuri nime `Main` ja võtmesõna `where`. Rida `main :: IO ()` määrab funktsiooni `main` tüübi, mis antud juhul on tüübile *void* vastav protseduuritüüp (`IO` määrab selle, et tegemist on protseduuritüübiga). Käsk `putStrLn` on lühend väljendist "*put String Line*", mis sisuliselt tähendab, et väljastatakse sõne, millele järgneb reavahetus. Sarnane käsk on `putStr`, kuid sel juhul jäetakse lõpust ära reavahetus.

## Loendid
Haskellis on olemas loendid ehk "listid". Tühja loendit tähistab `'''[]'''` (nurksulud). Loendi võib moodustada näiteks sisestades `[1, 2, 3, 4]`, mille tulemusel luuakse loend elementidega 1, 2, 3 ja 4. Sama loendi moodustamiseks võib kasutada ka konstruktsiooni `1 : 2 : 3 : 4 : []`, kus elemendid on ühendatud koolonitega ja viimane on tühja loendi tähis, mis märgib listi lõppu. Seda loendit saab moodustada ka aritmeetilise jada abil: [1 .. 4], kus 1 tähistab esimest elementi ja 4 viimast elementi ning jada samm on vaikimisi 1.

Sõnesid käsitletakse Haskellis ka kui tähemärkide loendeid. Näiteks "Tere, maailm!" on loend, mille elementideks on selles sõnes sisalduvad sümbolid.

### Loendite hõlmamine
Haskell toetab ka loendite hõlmamist (ingl. *list comprehension*). Näiteks
 [x ^ 2 | x x ^ 2 on avaldis ja `x <- [0, 0.5 .. 3]` on generaator. Generaatori parem pool peab alati olema loenditüüpi – näites sisuliselt loend [0, 0.5, 1, 1.5, 2, 2.5, 3].

## Mustrisobitus
Muster esitab skeemi, millega ette antud avaldist võrreldakse. Muster esitab vajalikku ehitust. Näiteks, kui oodatav väärtus, mis ette antakse, on mittetühi loend, siis saab näidisega **x : xs** kontrollida, et etteantavas loendis oleks vähemalt üks element. Kusjuures **x** tähistab loendi pead ehk esimest elementi ning **xs** tähistab loendi saba ehk ülejäänud elemente alates teisest elemendist. Näiteks loendi [5] korral, mis on üheelemendiline, on **x** väärtuseks 5 ja **xs** väärtuseks [].

Kohane näidis ilmestamaks loendi ehituse kontrolli oleks järgnev:

```
ymberPoord xs
  = let
      loppu x : xs            -- 1)
        = loppu xs ++ [x]
      loppu _                 -- 2)
        = []
    in
    loppu xs
```

Protseduur `ymberPoord` saab argumendina ette loendi. Kutsutakse välja `loppu` argumendiks saadud loendil. Kuna on `loppu` on defineeritud kaks korda (**1)** ja **2)**), siis valitakse näidiste sobitamise teel õige. Esimene näidis **1)** sobitub mittetühjade loenditega. Teine näidis **2)** on ainult alakriips ehk jokker, millega sobituvad kõik ette antavad loendid. Näiteks tühja loendi korral sobitamine näidisega **1)** ebaõnnestub, järelikult täidetakse kood, mis järgneb näidisele **2)**.

Näidise **1)** sobitumisel kutsutakse rekursiivselt välja `loppu` argumendiks olnud loendi sabale, kusjuures argumendiks olnud loendi pea eraldatakse ja lisatakse lõppu. Rekursiivsed pöördumised toimuvad, kuni argumendiks antakse mittetühi loend. Tühja loendi korral väljastatakse algne (protseduurile **ymberPoord** argumendina ette antud) loend.

### Rekursioon
Kuna Haskellis puuduvad tsüklid, siis nende asemel tuleb kasutada rekursiooni, sageli koos mustrisobitusega.

Ebaõnnestunud arvutuse märgiks on nn *bottom*, mida tähistatakse tagurpidi T-tähega (⊥). *Bottom* tekib programmi täitmisaegse vea või lõpmatu arvutuse korral. Kuna see võib juhtuda mistahes funktsiooni puhul, on *bottom* osa igast tüübist.

## Avaldiste liigid
### ''Let''-avaldis
*Let* avaldis võimaldab defineerida hulga lisamuutujaid protseduuri sees. Näiteks

```
tehe x
  = let
      ruut = x * x
      veerand = x / 4
    in
    ruut + veerand
```

See funktsioon tagastab etteantud argumendi veerandi ja ruudu summa. *Let*-avaldise puhul on olulised võtmesõnad **let** ja **in** ning pärast võtmesõna **in** peab järgnema avaldis.

### Lambdaavaldis
Lambdaavaldis on näiteks järgnev:
 viieKordne = \ x -> 5 * x
See võtab argumendiks arvu ning tagastab selle arvu viiekordsena. Näiteks, kui argumendiks on 6, tagastab see lambdaavaldis arvu 30. Interaktiivses keskkonnas jõuab sama tulemuseni sisestades
 (\ x -> 5 * x) 6
Lambdaavaldist alustav kaldkriips sarnaneb kreeka tähestiku tähe lambdaga – sellest tõenäoliselt ka nimi "lambdaavaldis".

### Valikuavaldis
Valikuavaldis võrdleb argumenti suurema hulga etteantud näidistega ning neist sobiva leidmisel täidab selle näidise kohta käivad käsud ning järgmiseid näidiseid enam ei võrdle.

```
faktoriaal n
  = case compare n 0 of
      GT
        -> ($!) faktoriaal (n - 1) * n
      EQ
        -> 1
      _
        -> error "Argument peab olema mittenegatiivne!"
```

Olulised võtmesõnad on **case** ja **of**. Argumendiks antud arvu võrreldakse nulliga (`compare 0 n`). Kui **n** on nullist suurem, siis kutsutakse rekursiivselt sama funktsiooni uuesti välja ühe võrra väiksemal argumendil. Kui **n** on nulliga võrdne, siis tagastatakse arv 1. Kui argument on negatiivne, siis kuvatakse veateade ("Argument peab olema mittenegatiivne!").

## Sisend ja väljund
Haskellis on ka vahendid standardväljundist lugemiseks ning sinna kirjutamiseks. Illustreerimiseks toome järgneva näite:

```
dialoog
  = do
      putStr "Sisesta palun oma nimi: "
      nimi <- getLine
      putStrLn ("Tere, " ++ nimi ++ ". Meeldiv tutvuda!")
```

See funktsioon küsib kasutajalt nime ning tervitab teda seejärel viisakalt nimepidi. Kasutatud on selle funktsiooni koostamisel **do-süntaksit**, mis võimaldab kirjeldada mitmeid tegevusi järjest.

## Viited