TIEA341 Funktio-ohjelmointi 1, kevät 2008 Luento 14: Monadit Antti-Juhani Kaijanaho Jyväskylän yliopisto Tietotekniikan laitos 21. tammikuuta 2008
Tyyppien tyypit eli luonteet engl. kind tyyppinimet, kuten Integer, Maybe ja Either, ovat tyyppikoostimia parametrisoimattomien tyyppikoostimien (kuten Integer) luonne on. parametrisoitujen tyyppikoostimien luonne esitetään funktiotyypeistä tutulla notaatiolla: tarkoittaa koostinta, joka ottaa yhden tyyppiparametrin (esim. Maybe) tarkoittaa koostinta, joka ottaa kaksi tyyppiparametria (esim. Either) luonnevika (kind error) on käännösvirhe siinä missä tyyppivirhekin esim. Maybe Either on luonnevikainen tyyppi
Funktori-luokka c l a s s Functor f where fmap : : ( a > b ) > f a > f b instance Functor Maybe where fmap f Nothing = Nothing fmap f ( Just x ) = Just ( f x ) instance Functor [ ] where fmap = map instance Functor IO where fmap f cmd = do x < cmd return ( f x )
funktoriluokkaan voivat kuulua -luonteiset tyyppikoostimiet funktoriluokka on säilöjen luokka Maybe-arvo on säilö, jossa on 0 tai 1 alkiota sisällä [] -arvo on säilö, jossa on rajaton määrä (nollasta äärettömään) alkioita sisällä IO-arvo eli komento on (hieman mielikuvitusta käyttäen) säilö, joka sisältää oman paluuarvonsa fmap soveltaa annettua funktiota säilön kaikille alkioille funktoriluokkaan kuuluvilla tyypeillä tulee päteä seuraavat aksioomat: fmap id = id fmap (f. g) = fmap f. fmap g (kääntäjällä on oikeus soveltaa niitä mm. optimointiin)
Monadi-luokka i n f i x l 1 >>, >>= c l a s s Monad m where (>>=) : : m a > ( a > m b ) > m b (>>) : : m a > m b > m b r e t u r n : : a > m a f a i l : : S t r i n g > m a m >> k = m >>= \_ > k f a i l s = e r r o r s i n s t a n c e Monad Maybe where ( J u s t x ) >>= k = k x N o t h i n g >>= k = N o t h i n g r e t u r n = J u s t f a i l s = N o t h i n g i n s t a n c e Monad [ ] where m >>= k = c o n c a t ( map k m) r e t u r n x = [ x ] f a i l s = [ ] i n s t a n c e Monad IO where
monadi on IO-tyypin yleistys monadiluokkaan kuuluva tyyppi on aina jollakin tavalla tulkittavissa komentotyypiksi Maybe-tyyppinen arvo on sivuvaikutukseton komento, jonka suoritus voi epäonnistua [] -tyyppinen arvo on sivuvaikutukseton komento, jonka arvo on (hyväntahtoisesti) epädeterministinen IO onkin jo tuttu return on komento, joka palauttaa argumenttinsa >>= suorittaa ensin vasemman operandinsa ja antaa sen paluuarvon oikealle operandilleen
Monadilait return a >>= k = k a m >>= return = m m >>=(\x k x >>= h) = (m >>= k) >>= h fmap f xs = xs >>= return. f (jos myös funktori)
do on kielioppimakeinen do e1 e1 do e1 e2 e1 >> do e2 do l e t e2 do do p < e1 e2 p < e1 e2 l e t i n do e2 e1 >>= \ p > do e2 l e t ok p = do e2 ok _ = f a i l " " i n e1 >>= ok (jos p ei ole hylkäävä) (jos p on hylkäävä) missä e1 jne ovat lausekkeita, p on hahmo ja ok on jokin nimi, joka ei ole muuten käytössä
Esimerkkejä l o o p = do l i n e < g e t L i n e c a s e l i n e o f [ ] > r e t u r n ( ) _ > do p u t S t r L n l i n e l o o p do ( x, y ) < z i p [ 1.. ] [ 2.. ] i f x mod 2 == 0 t h e n f a i l "" e l s e r e t u r n ( ) r e t u r n ( y, x ) do Just x < [ Just 1, Nothing, Just 2 ] r e t u r n x l o o p = g e t L i n e >>= \ l i n e > c a s e l i n e o f [ ] > r e t u r n ( ) _ > p u t S t r L n l i n e >> l o o p l e t ok ( x, y ) = ( i f x mod 2 == 0 t h e n f a i l "" e l s e r e t u r n ( ) ) >> r e t u r n ( y, x ) ok _ = f a i l " " i n z i p [ 1.. ] [ 2.. ] >>= ok l e t ok J u s t x = r e t u r n x ok _ = f a i l " " i n [ Just 1, Nothing, Just 2 ] >>= ok
Hyödyllisiä apufunktioita vakiokirjastosta s e q u e n c e : : Monad m => [m a ] > m [ a ] s e q u e n c e = f o l d r mcons ( r e t u r n [ ] ) where mcons p q = p >>= \ x > q >>= \ y > r e t u r n ( x : y ) sequence_ : : Monad m => [m a ] > m ( ) sequence_ = f o l d r (>>) ( r e t u r n ( ) ) mapm : : Monad m => ( a > m b ) > [ a ] > m [ b ] mapm f a s = s e q u e n c e ( map f a s ) mapm_ : : Monad m => ( a > m b ) > [ a ] > m ( ) mapm_ f a s = sequence_ ( map f a s ) (=<<) : : Monad m => ( a > m b ) > m a > m b f =<< x = x >>= f
Hyödyllisiä apufunktioita Control.Monad-kirjastosta j o i n : : ( Monad m) => m (m a ) > m a j o i n x = x >>= i d when : : ( Monad m) => B o o l > m ( ) > m ( ) when p s = i f p t h e n s e l s e r e t u r n ( ) u n l e s s : : ( Monad m) => B o o l > m ( ) > m ( ) u n l e s s p s = when ( n o t p ) s l i f t M : : ( Monad m) => ( a > b ) > (m a > m b ) l i f t M f = \ a > do { a < a ; r e t u r n ( f a ) } l i f t M 2 : : ( Monad m) => ( a > b > c ) > (m a > m b > m c ) l i f t M 2 f = \ a b > do { a < a ; b < b ; r e t u r n ( f a b ) } l i f t M 3, l i f t M 4 j a l i f t M 5 v a s t a a v a s t i ap : : ( Monad m) => m ( a > b ) > m a > m b ap = l i f t M 2 ( $ ) m u i s t a : ( $ ) f x = f x
Listan monaditulkinta hyväntahtoinen epädeterminismi do-lauseessa sijoitus listasta hahmoon (hahmo < lista) voidaan tulkita mielivaltaisen alkion valitsemisena listana ei kuitenkaan valita sellaista alkiota, jolla do-lauseen suoritus johtaa fail-kutsuun listatyyppisen do-lauseen paluuarvo on lista kaikista mahdollisista do-lauseen paluuarvoista, kun sijoitus tulkitaan valintana esim. do x < [ 0.. ] v a l i t s e j o k u l u o n n o l l i n e n l u k u when ( x mod 2 == 0) ( f a i l "" ) j o k a e i o l e p a r i l l i n e n r e t u r n ( x + 1) j a p a l a u t a s e n s e u r a a j a l a u s e k k e e n a r v o on l i s t a k a i k i s t a m a h d o l l i s i s t a p a l u u a r v o i s t a
MonadPlus modulissa Control. Monad c l a s s Monad m => MonadPlus m where mzero : : m a mplus : : m a > m a > m a i n s t a n c e MonadPlus Maybe where mzero = N o t h i n g Nothing mplus y s = y s x s mplus y s = x s i n s t a n c e MonadPlus [ ] where mzero = [ ] mplus = (++) msum : : MonadPlus m => [m a ] > m a msum x s = f o l d r mplus mzero x s g u a r d : : MonadPlus m => B o o l > m ( ) g u a r d p = i f p t h e n r e t u r n ( ) e l s e mzero
MonadPlus-luokkaan kuuluvat ne monadit, jotka tukevat epädeterminismiä mzero käyttäytyy yleensä kuten fail (mutta ei ota parametria) mplus suorittaa epädeterministisesti jomman kumman parametreistaan IO ei kuulu tähän luokkaan