do in e
≡ e
do e
1
; st in e
≡ let _e = e
1
in
(\ Unit : Unit . do st in e) _e
do . p = e
1
; st in e ≡ let _e = e
1
in
(\ p : typeof _e . do st in e) _e
do . p <- e
1
; st in e ≡ let _e = e
1
in
(\ p : basetypeof _e . do st in e) _e
do st end
≡ do st in Unit
Siin st t¨ahendab suvalist (t¨uhja v˜oi mittet¨uhja) do-notatsiooni lausete jada.
Operaator basetypeof sarnaneb operaatoriga typeof, kuid avaldise t¨u¨ubist
eemaldatakse k˜oik tipmisel tasemel olevad monaadid. Seega kui avaldis on
monaadilist t¨u¨upi (t¨apselt ¨uhe monaadiga), siis kasutatakse funktsiooni ra-
kendamise operaatori $ asemel operaatorit =<<.
N¨aeme, et erinevalt Haskellist on do-avaldisel kaks erinevat kuju —
do st in e ja do st end. Esimese variandi korral tagastatakse do-avaldise
tulemusena avaldise e v¨a¨artus. Siin on tagastatav v¨a¨artus ¨ulej¨a¨anud lause-
test eraldatud (ning ¨ulelaadimise t˜ottu ei ole seal return vaja kasutada),
erinevalt Haskellist, kus tagastatav v¨a¨artus m¨a¨aratakse do-konstruktsiooni
viimase lausega, mille ette tuleb enamasti return kirjutada. Teise varian-
di korral on do-avaldise tulemuseks ¨uhikt¨u¨ubi v¨a¨artus, mitte viimase lause
v¨a¨artus. Seega Fumontrixis on tagastusv¨a¨artus selgelt ¨ulej¨a¨anud lausetest
eraldatud, mis muudab koodi lugemise lihtsamaks.
Erinevalt Haskellist ei ole Fumontrixis monaadidel funktsiooni fail. Kui
do-avaldises n¨aidisesobitamine eba˜onnestub, siis on tulemuseks ⊥. Samas ei
ole see automaatne funktsiooni fail v¨aljakutsumine h¨adavajalik, kuna selle
sobitumise kontrolli ja eba˜onnestumise korral mingi funktsiooni v¨aljakutsu-
mise saab ka k¨asitsi teha, nagu j¨argnevas n¨aites (vt ka [1], jaotis 3.14):
do
st1;
. e <- f 10;
in
case e of
Just x -> do st2 in expr;
_
-> fail;
end;
See vastaks siis j¨argnevale koodile, kui automaatne fail v¨aljakutsumine oleks
olemas:
do
st1;
55
. Just x <- f 10;
st2;
in
expr
6.5.3
Mitme monaadi korraga kasutamine
Seni vaatlesime p˜ohiliselt juhtu, kus korraga on kasutusel maksimaalselt ¨uks
monaad. M˜onikord on vaja korraga mitut monaadi kasutada. Olgu
f : M
1
(M
2
a -> M
3
b) ja
x : M
4
(M
2
a)
Siis peaks funktsiooni rakendamisel olema
f x : M
1
(M
4
(M
3
b)) Siin M
1
, M
2
, M
3
ja M
4
on 0 v˜oi enama monaadi
kompositsioonid (need kompositsioonid ei pruugi ise olla monaadid).
Fumontrixis ongi v˜oimalik selliselt kasutada korraga mitut monaadi. Iga
funktsiooni rakendamise korral koostatakse kasutatavate monaadide monaa-
difunktsioonide p˜ohjal sobiv funktsiooni rakendamise operaator ja rakenda-
takse seda. Siin on erinevaid funktsiooni rakendamise operaatoreid palju roh-
kem kui 10, mis oli ¨uhe monaadi korral, aga programmeerija ei pea nende
p¨arast muretsema, kuna tavaline funktsiooni rakendamise operaator t¨o¨otab
k˜oigil neil juhtudel.
Kui M
1
= M
4
= M
3
= M ehk kasutusel on ainult ¨uks monaad, siis oleks
f x : M (M (M b)). Eelnevalt vaadeldud ¨uhe monaadiga juhu korral oli tu-
lemuseks f x : M b. Fumontrixis koondatakse sellisel juhul (monaadifunkt-
siooni join abil) k˜orvutisattuvad v˜ordsed monaadikonstruktorid. Esialgsete
kompositsioonide M
1
, M
4
, M
3
seest k˜orvuti olnud v˜ordseid monaadikonst-
ruktoreid ei koondata.
Kui funktsiooni rakendamise operaator on selliselt erinevate monaadide
kasutamiseks ¨ule laetud, siis saab do-avaldises vabalt kasutada vaheldumisi
erinevaid monaade. Sellisel juhul v˜oib l˜opuks do-avaldise t¨u¨up olla midagi
sellist:
M1 (M2 (M3 (M1 (M2 (M3 Int))))).
Siin M1, M2, M3 on ¨uksikud monaadid, mitte kompositsioonid.
Sellist t¨u¨upi v¨a¨artusest t¨aisarvu k¨attesaamiseks v˜oiks kirjutada run funkt-
siooni, mis t¨o¨otab k˜oigi v¨a¨artuste jaoks, mille t¨u¨up on kujul
m
1
(m
2
... (m
n
a)...),
ning annab v¨alja t¨u¨upi M a v¨a¨artuse, kus M on mingi monaad. Siin monaadid
m
1
, m
2
, ..., m
n
on mingi t¨u¨ubiklassi esindajad.
Selle jaoks on vaja, et iga monaadi m
i
jaoks oleks olemas teisendaja t¨u¨upi
m
i
a -> M a. Muidugi oleks v˜oimalik see teisendaja igal monaadi kasuta-
misel ilmutatult v¨alja kutsuda, kuid see l¨aheks kohmakaks. Mugavam oleks
56
v¨alja kutsuda run funktsioon ¨uhe korra terve do-avaldise jaoks.
GHC-s saab teha midagi sellist:
class ListRunnable a b | a -> b where
runList :: a -> [b]
newtype Stop a = Stop a
maybeToList :: Maybe a -> [a]
maybeToList (Just x) = [x]
maybeToList Nothing = []
instance ListRunnable (Stop a) a where
runList (Stop x) = [x]
instance ListRunnable a b => ListRunnable [a] b where
runList xs = concat (map runList xs)
instance ListRunnable a b => ListRunnable (Maybe a) b where
runList xs = concat (map runList (maybeToList xs))
(>=>) :: forall a1 r (m :: * -> *).
(Monad m) => m a1 -> (a1 -> r) -> m r
(>=>) = flip liftM
lr =
[1,2,3] >=> \ x ->
Just 700 >=> \ y ->
[10,20] >=> \ z ->
Stop (x + y + z)
Siin kasutame korraga kahte monaadi: [] ja Maybe. lr on t¨u¨upi
forall t. (Num t) => [Maybe [Stop t]]
ehk
[Maybe [Stop Integer]]
kui arvut¨u¨ubid monomorfsed oleks. Siis
runList lr :: [Integer]
Kuna GHC ei luba t¨u¨ubitaseme funktsioonides tulemuse t¨u¨ubi defineerimisel
vaikevariante kasutada (vt jaotist 5.3.5), siis on siin vaja konstruktor Stop
sisse tuua.
Fumontrixis saame sama n¨aite realiseerida t¨u¨ubitaseme funktsioonidega:
57