Nii lubatakse k¨ull Haskellis kirjutada, aga see ei ole vajaliku semantikaga,
kuna Haskellis on let-deklaratsioonid alati (potentsiaalselt) rekursiivsed ja
teises let-is v˜ordusm¨argist paremal olevad muutuja r kasutused viitavad
rekursiivselt sellele samale teise let-i muutujale r, mitte esimese let-i omale.
Seega ei j¨a¨a muud v˜oimalust kui nimetada ¨uks muutujatest ¨umber, n¨ai-
teks esimese r asemel r0 v˜oi teise r asemel r’. Programmeerija jaoks on
muutujanimede v¨aljam˜otlemine ja meeldej¨atmine suur vaev ning selliste sar-
naste t¨ahenduste ja nimedega muutujate nimed v˜oivad kergesti segamini min-
na. Seega programmeerija v˜oib kogemata kasutada vale muutujat, kuigi ta
teadis, et vaja on kasutada ainult viimasena defineeritud muutujat. Kui keel
lubaks defineerida mitu sama nimega muutujat, siis seda probleemi ei esineks,
kuna k¨attesaadav on ainult viimane definitsioon.
Seet˜ottu Fumontrixis on let-deklaratsioonid vaikimisi mitterekursiivsed
ning ¨uhes let-avaldises saab defineerida mitu samanimelist muutujat. K¨at-
tesaadav on kasutamise kohale eelnevatest samanimeliste muutujate definit-
sioonidest viimane. Fumontrixis v˜oib eelneva n¨aite kirjutada nii:
let
r = x % m;
r = if (r*2 < m)
r
(r - m);
in
f r
Rekursiivse v¨a¨artuse defineerimiseks saab kasutada rec-deklaratsiooni:
let
rec ones : List Int = 1 :: ones;
in
f ones
Seega Fumontrixis saab koodi lugeda ¨ulevalt alla, kuna viidata saab vaid
eelnevatele definitsioonidele. Vaid rec-deklaratsiooni korral on v˜oimalik vii-
data pooleliolevale definitsioonile, kuid sel juhul tuleb lisaks muutuja nimele
definitsiooni alguses deklareerida ka muutuja t¨u¨up. Seega kasutamise hetkeks
on muutuja t¨u¨up juba lugejale teada.
2.2
T¨
u¨
ubiinferents
Haskellis kasutatakse Hindley-Milneri t¨u¨ubituletusalgoritmi ([1], jaotis 4.5).
Et eristada sellist unifitseerimist ja v˜orrandite lahendamist kasutavat t¨u¨ubi-
tuletust tavalisest (lihtsamast) t¨u¨ubituletusest, nimetame selles t¨o¨os esimest
9
t¨u¨ubiinferentsiks. T¨u¨ubiinferentsiga sarnast meetodit liikide m¨a¨aramiseks ni-
metame liigiinferentsiks.
Hindley-Milneri t¨u¨ubiinferentsi korral m¨a¨arab translaator paljudele muu-
tujatele ise t¨u¨ubi, seega programmeerija ei pea alati t¨u¨ubiannotatsioone li-
sama. Samas muudab see koodi lugemise raskemaks, kuna t¨u¨ubiinferentsi
algoritmi peast rakendamine v˜oib keerulisematel juhtudel liiga raskeks osu-
tuda. Samuti s˜oltub muutuja t¨u¨up tema kasutustest, mis v˜oivad esineda koo-
dis selle definitsioonist palju hiljem. See l¨aheb vastuollu ¨ulalt alla loetavuse
p˜ohim˜ottega.
Haskell lubab lisada muutujadefinitsioonidele t¨u¨ubisignatuure, kuid nen-
de kasutamine ei ole kohustuslik. Funktsiooni t¨u¨ubisignatuur peab sisalda-
ma ka tagastusv¨a¨artuse t¨u¨upi, mis mitterekursiivsete funktsioonide korral ei
oleks tegelikult vajalik, kuna tagastusv¨a¨artuse t¨u¨ubi saab lihtsalt argumen-
tide t¨u¨upide j¨argi leida.
Fumontrixis (nagu ka GHC-s) on v˜oimalik kasutada k˜orgemat j¨arku uni-
versaalselt kvantifitseeritud t¨u¨upe. ¨
Uldjuhul on aga k˜orgemat j¨arku t¨u¨upide
korral t¨u¨ubiinferents mittelahenduv ([2], jaotis 8.7.4.2). Ka GHC-s on vaja
teist ja k˜orgemat j¨arku t¨u¨upide kasutamisel annotatsioonid lisada.
Seet˜ottu Fumontrixis Haskelli stiilis t¨u¨ubiinferentsi ei kasutata. Selle ase-
mel tuleb λ-seotud muutujate sissetoomisel t¨u¨ubid juurde kirjutada. N¨aiteks:
succ = \ x : Int . x + 1
let- ja case-seotud muutujate korral t¨u¨upi juurde pole vaja lisada (v.a rec-
deklaratsioonide korral), kuna seal on muutuja v¨a¨artustamiseks kasutatav
avaldis kohe juures ja selle t¨u¨up on teada.
Samuti ei ole Fumontrixis liigiinferentsi. Seega tuleb kvantoriga t¨u¨ubi-
muutuja sissetoomisel lisada liigiannotatsioon. Liigiannotatsiooni v˜oib ¨ara
j¨atta, kui liigiks on *.
2.3
Skoopide v˜
ordv¨
a¨
arsus
Haskellis on peataseme skoop v¨aga erinev tavalisest alamskoobist. T¨u¨ubiklas-
se, esindajaid, algebralisi andmet¨u¨upe, t¨u¨ubis¨unon¨u¨ume on v˜oimalik definee-
rida ainult peatasemel. Seega need definitsioonid on k˜oik globaalselt k¨atte-
saadavad ja kogemata on v˜oimalik mingis kohas kasutada definitsiooni, mida
tegelikult ei tohiks seal kasutada. Mingil m¨a¨aral on moodulite abil v˜oimalik
definitsioone peita, kuid v¨aga v¨aikesteks juppideks ei ole m˜otet koodi t¨ukelda-
da, seega see on kasutatav p˜ohiliselt suuremate alamskoopide jaoks. Samuti ei
ole ¨uhes Haskelli moodulis v˜oimalik defineerida t¨u¨ubiklassi, andmet¨u¨upi jne,
millega samanimeline olem (nimetame olemiks k˜oiki asju, millele on v˜oimalik
10
koodis mingi identifikaatoriga viidata) on juba m˜ones kasutatavas moodulis
defineeritud (ja eksporditud).
Fumontrixis on t¨u¨ubiklasse, t¨u¨ubitaseme funktsioone, algebralisi andme-
t¨u¨upe jne v˜oimalik defineerida k˜oigis skoopides — nii peatasemel kui let-
avaldistes. Samuti on v˜oimalik defineerida uut olemit, mille nimi langeb kok-
ku m˜one olemasolevaga. Sellisel juhul alamskoobis enam vanale olemile selle
nimega viidata ei saa, kuid see v˜oib j¨a¨ada kaudselt k¨attesaadavaks.
2.4
K˜
orvalefektid, erindid ja laiendatud t¨
u¨
ubid
Paljudes keeltes v˜oivad avaldiste v¨a¨artused sisaldada lisaks tavalisele v¨a¨ar-
tusele k˜orvalefekte. N¨aiteks Java funktsioon, mille tagastusv¨a¨artuse t¨u¨ubiks
on boolean, v˜oib enne t˜oev¨a¨artuse tagastamist teha mingeid k˜orvalefekte
(muuta muutujate v¨a¨artusi, teha sisendit-v¨aljundit jne) v˜oi tagastada t˜oe-
v¨a¨artuse asemel midagi muud (nt erindi, m˜onede teiste t¨u¨upide korral ka
nullviida). Seega Java t¨u¨up boolean on v¨aga erinev matemaatilisest (kahe-
elemendilisest) t˜oev¨a¨artust¨u¨ubist. Samas v˜oib ka Java funktsioon tagastada
t˜oev¨a¨artuse ilma erindita ja k˜orvalefekte tegemata, seega Java t¨u¨up boolean
sisaldab alamt¨u¨ubina matemaatilist t˜oev¨a¨artust¨u¨upi. V˜oime ¨oelda, et Javas
on kasutusel laiendatud t˜oev¨a¨artust¨u¨up.
Kui t¨u¨ubis¨usteem toetab ainult laiendatud boolean-i, siis me ei saa lasta
t¨u¨ubis¨usteemil staatiliselt kontrollida, et funktsiooni tagastusv¨a¨artus kuulub
puhta boolean-i t¨u¨upi. Et garanteerida, et tagastusv¨a¨artus ei sisalda erin-
deid, on vaja k¨asitsi kontroll koodi sisse kirjutada. Kuid sellisel juhul on v˜oi-
malik t¨u¨ubiveast teada saada alles p¨arast koodi k¨aivitamist, seega tegemist
on d¨unaamilise t¨u¨ubikontrolliga.
Kui me soovime, et keel oleks staatilise t¨u¨ubikontrolliga (nagu Haskell),
siis peaks ka puhast boolean-i ja laiendatud boolean-i saama staatiliselt
eristada, vastasel korral on t¨u¨ubikontroll ainult pooleldi staatiline ja pooleldi
d¨unaamiline. Kui on olemas puhas boolean, siis sellest saab vajaduse korral
laiendatud boolean-i defineerida (n¨aiteks monaadide abil), vastupidine ei ole
aga v˜oimalik nii, et staatiline t¨u¨ubikontroll s¨ailiks.
Vaatleme, milleks on kasulik, kui on olemas puhas boolean (mitte ai-
nult laiendatud boolean). N¨aiteks olgu tegemist kr¨uptoloogilise teegiga. Seal
on mingi funktsioon, mis kasutab salajasi andmeid ja tagastab kasutajale
ainult ¨uhe biti informatsiooni. Kasutaja v˜oib olla pahatahtlik ja ¨uritab sala-
jaste andmete kohta infot saada. Kui funktsiooni tagastust¨u¨ubiks on puhas
boolean, siis ta ei saa ¨uhe p¨aringuga k¨atte rohkem kui ¨uhe biti salajast in-
formatsiooni. Kui funktsiooni tagastust¨u¨ubiks on laiendatud boolean, siis
ta v˜oib turvaauke ¨ara kasutades panna funktsiooni tagastama erindit, mis
sisaldab palju rohkem kui ¨uhe biti salajast informatsiooni.
11