Siis saab kasutada avaldisi nagu type f (value 2) ja type f (value True).
See s¨untaks on natuke kohmakas, kuna t¨u¨ubitaseme ja andmetaseme vahel
liikumiseks tuleb kasutada v˜otmes˜onu type ja value. Soovi korral v˜oib lisada
s¨untaktilist suhkrut, mis teeb selle tasemete vahel liikumise mugavamaks, siis
v˜oiks n¨aiteks kirjutada eelnevalt vaadeldud avaldised kujul ’f 2 ja ’f True
ning t¨u¨ubitaseme funktsiooni definitsioonis type x asemel @x. Praegu ei ole
lihtsuse huvides seda keelde lisatud.
Liiki @ -> @ t¨u¨ubitaseme funktsioonid ehk need, mis seavad v¨a¨artusele
vastavusse v¨a¨artuse, sarnanevad v¨a¨artustaseme funktsioonidega. K˜oiki v¨a¨ar-
tustaseme funktsioone saab ka t¨u¨ubitaseme funktsioonidena defineerida. Sa-
mas v¨a¨artustasemel saab defineerida ainult neid funktsioone, millel on t¨u¨u-
bis¨usteemis olemas t¨u¨up.
T¨u¨ubitasemel on v˜oimalik defineerida ka selliseid funktsioone, millel t¨u¨u-
bis¨usteemis t¨u¨upi ei ole (selline on ka funktsioon f viimases n¨aites). See ei
t¨ahenda staatilise t¨u¨ubikontrolli puudumist. Sellisel funktsioonil on olemas
implitsiitne t¨u¨up. Iga t¨u¨ubi jaoks saab staatiliselt kontrollida, kas see t¨u¨up
sobib antud funktsiooni argumendiks ja, kui sobib, siis saab staatiliselt m¨a¨a-
rata rakendamise tulemuse t¨u¨ubi.
Kuna t¨u¨ubitaseme funktsioonil t¨u¨ubis¨usteemis t¨u¨upi ei ole, siis ei saa seda
anda argumendiks andmetaseme funktsioonile. K¨ull aga saab seda anda ar-
gumendiks teisele t¨u¨ubitaseme funktsioonile (n¨aiteks liiki (@ -> @) -> @).
Samuti saab (n¨aiteks liiki @ -> @ -> @) t¨u¨ubitaseme funktsiooni tulemu-
seks olla teine t¨u¨ubitaseme funktsioon, seega saab kasutada curried-kujul
t¨u¨ubitaseme funktsioone. Seega t¨u¨ubitaseme funktsioonid on t¨u¨ubitasemel
first-class.
5.2.2
T¨
u¨
ubitaseme funktsioonid ja t¨
u¨
ubiinferents
Kuna t¨u¨ubitaseme funktsiooni korral ei ole vaja m¨a¨arata argumendi t¨u¨upi
(liik tuleb siiski m¨a¨arata), siis saab t¨u¨ubitaseme funktsioone kasutada t¨u¨u-
biinferentsi asemel. N¨aiteks v˜oime andmetaseme funktsiooni
double = \ x : Int . x + x;
asemel defineerida t¨u¨ubitaseme funktsiooni:
type double = \ x : @ . value (type x) + (type x);
Erinevalt andmetasemest ei ole t¨u¨ubitasemel funktsiooni argumendi t¨u¨upi
m¨a¨aratud. Samas selle funktsiooni rakendamisel mingit muud t¨u¨upi kui Int
argumendile tuleb staatiliselt t¨u¨ubiviga (kuna muud t¨u¨upi v¨a¨artust ei saa
funktsiooni + argumendiks anda).
30
Samas v˜oib juhtuda, et programmeerija kirjutab kogemata + asemel ||.
Esimeses definitsioonis tuleb see viga kohe v¨alja, kuna x t¨u¨ubiks on m¨a¨a-
ratud Int, kuid || ootab argumendiks Bool-t¨u¨upi v¨a¨artust. T¨u¨ubitaseme
funktsiooni korral ei tule see viga definitsiooni kontrollimisel v¨alja. Definit-
sioon
type double = \ x : @ . value (type x) || (type x);
on t¨aiesti t¨u¨ubikorrektne (ja rakendatav Bool-t¨u¨upi v¨a¨artustele), kuid see
erineb sellest, mida programmeerija plaanis kirjutada. Viga tuleb v¨alja alles
siis, kui funktsiooni ¨uritatakse rakendada Int-t¨u¨upi v¨a¨artusele, kuid n¨aiteks
teegi kirjutamisel v˜oib juhtuda, et m˜onda defineeritud funktsiooni ei raken-
data kordagi (selle teegi koodis).
Sama probleem esineb ka t¨u¨ubiinferentsi korral. Kui Haskellis kirjutada
definitsioon
double x = x || x
siis translaator ei tea, et programmeerija plaanis argumendi t¨u¨ubiks Int, ning
t¨u¨ubiviga j¨a¨ab avastamata.
See on ka ¨uks p˜ohjus, miks Fumontrixis t¨u¨ubiinferentsi ei kasutata. Vaja-
duse korral saab kasutada t¨u¨ubitaseme funktsioone, kuid siis peab program-
meerija lihtsalt ettevaatlikum olema. Haskellis kasutatakse t¨u¨ubiinferentsi
alati ja programmeerija ei saa seda keelata. GHC-s saab k¨ull funktsiooni ar-
gumendile t¨u¨ubiannotatsiooni lisada, kuid programmeerija v˜oib selle m˜ones
kohas ¨ara unustada. Translaator sellisel juhul ei hoiata ja t¨u¨ubivead v˜oivad
j¨a¨ada avastamata.
T¨u¨ubitaseme funktsioonide korral esineb veel ¨uks probleem. N¨aiteks de-
finitsioon
type tb = \ x : @ . value 3 + True;
t¨u¨ubiviga ei anna, kuigi selles sisalduv avaldis 3 + True ei ole t¨u¨ubikor-
rektne. Siin on lihtsalt tegemist t¨u¨ubitaseme funktsiooniga, mis ei ole ¨uhegi
argumendi korral defineeritud, samas kui funktsioon ise on defineeritud. Iga
katse seda funktsiooni rakendada annab t¨u¨ubivea. T¨u¨ubiviga on t¨u¨ubitaseme
analoog andmetaseme ⊥-le. Vaadeldaval juhul alamavaldis ei s˜oltu funktsioo-
ni argumendist x, kuid ¨uldjuhul v˜oib s˜oltuda ning sel juhul on juba keeruline
kindlaks teha, kas funktsioon annab iga argumendi korral t¨u¨ubivea. Fumon-
trixis sellist anal¨u¨usi ei ¨uritata teha. Selliste vigade v¨altimiseks v˜oib t¨u¨ubita-
seme funktsiooni argumendist mittes˜oltuvad alamavaldised t¨u¨ubilambda alt
v¨alja tuua eraldi definitsioonidesse. See muudab k¨ull koodi hakitumaks.
Liigivead ja tundmatute t¨u¨ubiidentifikaatorite kasutused tulevad siiski
kohe t¨u¨ubitaseme funktsiooni defineerimisel v¨alja, kuna need ei s˜oltu kunagi
argumendist (argumendi liik on fikseeritud).
31
5.3
T¨
u¨
ubitaseme funktsioonid Haskellis
5.3.1
Ad-hoc-pol¨
umorfsed funktsioonid
Haskell 98-s on olemas t¨u¨ubis¨unon¨u¨umid, mille abil on v˜oimalik defineerida
ainult parameetriliselt pol¨umorfseid t¨u¨ubitaseme funktsioone. Samuti on v˜oi-
malik kasutada t¨u¨ubiklasse, mille abil saab defineerida pol¨umorfseid funkt-
sioone. GHC-s on selliselt defineeritavate pol¨umorfsete funktsioonide hulk
palju suurem kui Haskell 98-s. Kuna t¨u¨ubis¨unon¨u¨umid on Haskellis v¨aga pii-
ratud v˜oimalustega, siis neid me selles t¨o¨os l¨ahemalt ei vaatle ning Haskelli
v˜oi GHC t¨u¨ubitaseme funktsioonide [5] all peame silmas t¨u¨ubiklasside abil
defineeritavaid pol¨umorfseid funktsioone.
Haskell 98-s olemas ainult ¨uhe parameetriga t¨u¨ubiklassid, mille abil on
v˜oimalik defineerida ad-hoc-pol¨umorfseid funktsioone (erinevat t¨u¨upi argu-
mentide korral v˜oib tulemuse v¨a¨artuse leidmiseks argumendi v¨a¨artuse p˜oh-
jal kasutada erinevat koodi), mille tulemuse t¨u¨up s˜oltub argumendi t¨u¨ubist
ainult parameetriliselt pol¨umorfselt (tulemuse t¨u¨up tuleb avaldada ¨uhe t¨u¨u-
biavaldisena, mis v˜oib argumendi t¨u¨upi sisaldada ainult parameetrina ehk
t¨u¨ubimuutujana). N¨aiteks v˜oime kirjutada funktsiooni, mis nii t˜oev¨a¨artuste-
le kui t¨aisarvudele seab vastavusse t¨aisarvu, kuid erineva koodiga:
class C1 a where
f1 :: a -> Integer
instance C1 Bool where
f1 b = bool2int b
instance C1 Integer where
f1 x = x
Siis n¨aiteks f1 (3::Integer) annab tulemuseks 3 ja f1 True annab tule-
museks 1. Siin eeldame, et eelnevalt on olemas definitsioonid
int2bool x = x /= 0
bool2int b = if b then 1 else 0
Samuti v˜oime Haskell 98-s kirjutada funktsiooni, mis t˜oev¨a¨artustele seab
vastavusse t˜oev¨a¨artuste listi ning t¨aisarvudele t¨aisarvude listi, kuid erineva
koodiga:
class C2 a where
f2 :: a -> [a]
instance C2 Bool where
f2 b = [b,b]
instance C2 Integer where
f2 x = [x,x,x]
32
Dostları ilə paylaş: |