2011-07-06 14 views
9

Kafa karıştırıcı bir soru için kafa karıştırıcı bir başlık! A) monadları, b) IO monadını, c) Cont monad (Control.Monad.Cont) ve d) ContT devamı transformatör monadını anlıyorum. Ben tüm fonksiyonları Katkı monad (Cont r a) olan bir program yazmak için nasıl anlamak ve ben bir program yazmak için anlamak - (Bu soruyu cevaplamak için yeterli olsa. Ve belli belirsiz genel anlamda monad transformatörleri anlamak) Burada tüm işlevleri birleştirilmiş Cont/IO monadında (ContT r IO a) bulunur.Devam Monitörü içindeki IO monadından kaçış

Ama bazı fonksiyonları kombine Katkı/IO monad (ContT r IO a) ve diğer fonksiyonları olan bir program yazmak nasıl merak ediyorum sadece Devam monad (Cont r a) içindedir. Temel olarak, tüm programı devam tarzında yazmak istiyorum, ancak sadece IO monadını gerekli yerlerde kullanıyorum ("normal" Haskell kodunda olduğu gibi, sadece IO monadını gerekli olduğu yerde kullanıyorum). foo IO gerekiyor ancak bar saf

foo :: Int -> IO Int 
foo n = do 
    let x = n + 1 
    print x 
    return $ bar x 

bar :: Int -> Int 
bar m = m * 2 

Not olun: Örneğin

olmayan devamı tarzında bu iki işlevi, düşünün. Şimdi tamamen devamı monad kullanarak bu kod yazmak için nasıl düşündüm, ama ben de bar aracılığıyla IO iplik için gerekli: 'Ben devamı tarzında tüm kodumu istiyorsun ama don

foo :: Int -> ContT r IO Int 
foo n = do 
    let x = n + 1 
    liftIO $ print x 
    bar x 

bar :: Int -> ContT r IO Int 
bar m = return $ m * 2 

t, IO monadını gerektirmeyen işlevler üzerinde kullanmak zorunda. Temelde, böyle bar tanımlamak istiyorum:

bar :: Int -> Cont r Int 
bar m = return $ m * 2 

Ne yazık ki, bir ContT r IO a monad fonksiyonu (foo) içinden bir Cont r a monad fonksiyonunu (bar) aramak için bir yol bulmak mümkün değil. Dönüştürülmemiş bir monayı dönüştürülmüş olana "kaldırmak" için herhangi bir yol var mı? yani foo'daki "bar x" satırını bar :: Int -> Cont r Int doğru şekilde arayabilmesi için nasıl değiştirebilirim? Control.Monad.Class devreye giriyor

cevap

17

budur haline getirin çalışabilir neler monad içinde polimorfik bar.

bar :: MonadCont m => Int -> m Int 
bar m = return $ m * 2 

Not sayfanın alt kısmında örneklerini liste MonadCont örnekleri anda bilinen bu gösteriyor ki oluşturulan dokümanlar hem Cont r hem de Monad m => ContT r m içerir. Ayrıca, MonadCont sınıfı, devam özelliklerini kullanmak için gerekli olan callCC işlevini tanımlar. Bu, bu örnekte bulunmasa bile, bar'daki devamların tam ifadesini kullanabileceğiniz anlamına gelir.

Bu şekilde, MonadIO kısıtlamasına sahip olmadıkları ve türlerinin açıkça IO belirtmedikleri için IO'yu makul olarak kullanamayan işlevler yazarsınız. Fakat onlar içinde monad olarak çalıştıkları polimorfiktirler, böylece IO'yu içeren bağlamlardan önemsiz olarak çağrılabilirler.

+1

Teşekkürler. Bu işe yarıyor. Ben de tam olarak istediğim şeyi ('Bar' değiştirmek zorunda değildim) verdi kendi çözümumu buldum: 'liftCont :: Cont (m r) a -> ContT r m a'; 'liftCont c = ContT $ runCont c'. Çözümüm 'Cont' un paketini açar ve' ContT' oluşturur. Çözümünüzün daha güzel olduğunu düşünüyorum çünkü bu polimorfiktir ve veri yapılarının gerçek manipülasyonunu gerektirmez, bu yüzden sizin için işaretleyin. Ama ben 'bar' değiştiremezseniz yararlıdır, çünkü benim başka bir yanıt olarak benim postalayacağım. Ayrıca "bar" da IO kullanmanın imkansız olduğunu açıklamak için +1. – mgiuca

5

Bu (Bar değiştirmek zorunda kalmadan) tam olarak ne istediğini yapar bulundu:

liftCont :: Cont (m r) a -> ContT r m a 
liftCont = ContT . runCont 

Bu Cont açar ve bir ContT oluşturur.

Sonra BarFoo çağrı yapmayı liftCont kullanabilirsiniz: Bu Carl'ın çözümü daha "güzel" olduğunu sanmıyorum

foo n = do 
    let x = n + 1 
    liftIO $ print x 
    liftCont $ bar x 

(Ona kene verdi), ama bundan dolayı burada yayınlanmıştır türünü değiştirmeden Bar kullanmanıza izin verir, böylece Bar'u değiştiremezseniz yararlıdır. (Muhtemelen olsa kötü performansa sahiptir.)