Diagnostikaga olekuteisendusmonaad

Kodutöö eesmärk on kirjutata olekuteisendusmonaadi laiendus, mis lisaks tavalistele olekuteisendusmonaadi liidesele (vt. Control.Monad.State) realiseerib allkirjeldatud lisa funktsionaalsuse

Kõigepealt tuleb defineerida uuele monaadile vastav tüübikonstruktor:

newtype StateMD s a = ...

Tüübimuutujad s ja a omavad standartset tähendust: s on oleku tüüp ning a on väärtuse tüüp.

Seejärel tuleb tüüp teha klasside Monad ja MonadState instantsideks. Kuna klass MonadState ei ole prelüüdis defineeritud, tuleb vastav teek importida:

import Control.Monad.State

Klass MonadState kasutab "funktsionaalseid sõltuvusi", mis on Haskell 98 laiendus. Seetõttu tuleb kompileerimisel anda ghc-le vajalikud lipud; lihtsaim viis selleks on lisada järgnev "pragma" oma faili esimesse ritta:

{-# OPTIONS -fglasgow-exts #-}

Järgnevalt kirjeldame lisafunktsionaalsust, mida realiseeritav monaad peab toetama:

Monaadi "jooksutamine" ja veateated

Realiseeritavas monaadis võivad arvutused ebaõnnestuda ja ja monaad peab olema suuteline vigadega ise hakkama saama. Mingeid eraldi olevaid veatöötlusoperatsioone pole vaja reliseerida, kuid klassis Monad olev meetod fail tuleb defineerida selliselt, et ei annaks täitmisaegset erindit (mis ta teeb vaikimisi definitsiooni korral).

Monaadil tuleb realiseerida järgnev primitiivfunktsioon:

runStateMD :: StateMD s a -> s -> Either String (a, s)

See funktsioon saab argumentidena monaadilise arvutuse ja algoleku ning väljastab Left str kui arvutus ebaõnnestus (ja viga tekitati funtsiooni fail väljakutsega argumendile str). Kui arvutus õnnestus, siis väljastatakse Right (x,s'), kus x on lõppväärtus ning s' on lõppolek.

Diagnostika

Monaad StateMD peab koguma informatsiooni enda primitiivoperatsioonide väljakutsete arvu kohta. Selleks tuleb kirjutada funktsioon diagnostics, millel on järgmine tüüp:

diagnostics :: StateMD s String

See funktsioon peab loendama seni kasutatud bind (>>=) ja return operatsionide arvu (samuti teiste primitiivsete funktsioonide arvu; sh. väljakutsutav diagnostics ise). Lisaks tuleb kirjutada funktsioon:

annotate :: String -> StateMD s a -> StateMD s a

mis lubab kasutajal annoteerida "alamarvutusi". Kõik primitiivfunktsioonid (return, bind (>>=), diagnostics, get, put, ) loadState, saveState) ja kasutajaannoteeritud arvutused peavad olema diagnostika väljundis.

Näide:

do return 3 >> return 4
   return 5
   diagnostics

väljastab "[bind=3, diagnostics=1, return=3]". Paneme tähele, et >> on realiseeritud kasutades >>= ja seetõttu läheb kirja kui bind.

Näide:

do annotate "A" (return 3 >> return 4)
   return 5
   diagnostics

väljastab "[A=1, bind=3, diagnostics=1, return=3]".

Olekute ajalugu

Viimane lisafunktsionaalsus mida monaad peab toetama on jooksva oleku salvestamine ja eelneva oleku taastamine. Lisa oma programmi alljärgnev klassidefinitsioon ning tee StateMD selle instantsiks.

class MonadState s m => StoreState s m | m -> s where
   saveState :: m ()
   loadState :: m ()

Olekute salvestamine ja lugemine peab olema realiseeritud magasinina; so. oleku salvestamine paneb hetkeoleku magasini tippu ning lugemisel võetakse olek magasini ülemisest pesast. Kui loadState kutsutakse välja tühja situatsioonis kui magasin on tühi, siis monaadi arvutus ebaõnnestub (nagu eelpool kirjeldatud).

Näide:

do i1 <- get ; saveState
   modify (*2)
   i2 <- get ; saveState
   modify (*2)
   i3 <- get ; loadState
   i4 <- get ; loadState
   i5 <- get
   return [i1, i2, i3, i4, i5]

väljastab algoleku 1 korral listi [1,2,4,2,1].


Varmo Vene