2015-04-05 17 views
26

Typeclassopedia 'ın Monad Transformers bölümünde açıklanmaktadır:.oluşturma monad'ların v Uygulanabilir Functors

Maalesef monads uygulamalı functors (henüz gerekmiyorsa Uygulamalı kullanmak için başka bir nedenle kadar güzel beste yok >>= ve <*>, yukarıdaki deyimi türleri baktığımızda Monad sağlar tam güç)

bana açık değildir.

(<*>) :: Applicative f => f (a -> b) -> f a -> f b 
(>>=) :: Monad m => m a -> (a -> m b) -> m b 

Lütfen "monad'ların, uygulama klavyeleri kadar güzel oluşturmadığını" açıklayınız.

Bu answer'u okudum, ancak anlamama yardımcı olacak bir örnek verebilir misiniz?

+5

"Monad Dönüştürücüler" bölümünde bazı örnekler (Uygulamalı f Uygulamalı g) => 'bu Uygulamalı (Cı-fg)' ancak durum böyle bu '(Monad f, Monad g) => Monad (C fg)', burada veri C fga = C (f (ga)) 'dir. Durumun neden böyle olduğuna dair daha derin bir anlayış istiyorsanız, ikinci örneği yazmayı deneyebilirsiniz. – user2407038

+1

Ayrıca bakınız [Mantarlar kompozisyon altında kapalı (prova ile) kapalı olmadığını gösteren somut örnek?] (Http://stackoverflow.com/q/13034229/1333025) –

cevap

36

* -> * türlerinin hangi tür "oluşturabileceğini" içeren çeşitli kavramlar vardır. Daha önemli olan, onları "sıralı olarak" oluşturabilmenizdir.

newtype Compose f g x = Compose { getCompose :: f (g x) } 

Burada Compose tür (* -> *) -> (* -> *) -> (* -> *) çok functors herhangi iyi kompozisyon gibi sahip olduğunu görmelisin yapabilirsiniz.

Yani soru şu: Aşağıdaki gibi yasalara uyan durumlar vardır?

instance (Applicative f, Applicative g) => Applicative (Compose f g) 
instance (Monad f,  Monad g)  => Monad  (Compose f g) 

Ve monads yanı applicatives olarak oluşturmak yok neden olarak kısa cevap ilk derece ikinci yazılabilir kullanımın oldukça olamaz olduğunu. Hadi deneyelim!


Biz Functor

instance (Functor f,  Functor g)  => Functor  (Compose f g) where 
    fmap f (Compose fgx) = Compose (fmap (fmap f) fgx) 

ile ısınabilirler Burada elimizden fmap bir fmapf -ed çünkü biz gerekiyor gibi biz katmanları f ve g içinden geçebilir görüyoruz. Benzer bir oyun pure

instance (Applicative f, Applicative g) => Applicative (Compose f g) where 
    pure a = Compose (pure (pure a)) 

ile oynanan ve (<*>) yaparken dikkatli bakarsan biz fmap ve pure ikisi ile kullanılan aynı numara, zor görünür. umut edebileceğiniz gibi

Compose fgf <*> Compose fgx = Compose ((<*>) <$> fgf <*> fgx) 

Her durumda, biz tam olarak f ve g katmanları "yoluyla" İhtiyacımız operatörleri zorlayabilir.

Ama şimdi en Monad bir göz atalım. Monad'u (>>=) aracılığıyla tanımlamaya çalışmak yerine join aracılığıyla çalışacağım.Monad uygulamak için biz newtype gürültü kapalı şerit halinde olduğumuzu net olabilir Bu noktada

join :: f (g (f (g x))) -> f (g x) 

ihtiyaç

join_f :: f (f x) -> f x -- and 
join_g :: g (g x) -> g x 

kullanılarak

join :: Compose f g (Compose f g x) -> Compose f g x 

uygulamak gerekir ya da ne problem --- biz sadece s veya g s katmanlarınakatmanın nasıl katılacağını biliyoruz ama burada arası interwoven'i görüyoruz. Ne bulacaksınız biz Yerdeğiştirme özelliği

class Commute f g where 
    commute :: g (f x) -> f (g x) 

ihtiyaç ve şimdi

join :: f (g (f (g x))) -> f (g x) 
join fgfgx = fgx where 
    ffggx :: f (f (g (g x))) 
    ffggx = fmap commute fgfgx 
    fggx :: f (g (g x)) 
    fggx = join_f ffggx 
    fgx :: f (g x) 
    fgx = fmap join_g fggx 

olarak tanımlanan (agnostik newtype) join ile

instance (Monad f, Monad g, Commute f g) => Monad (Compose f g) 

uygulamak olmasıdır

Peki, bütün bunlar ne anlama geliyor? s her zamanCompose, ancaks Compose yalnızca katmanları Commute olduğunda.

commute katmanları ne zaman yapabiliriz? Burada tüm bu demektir olduğuna inanıyoruz, bu yana

instance Commute ((->) x) ((->) y) where 
    commute = flip 

instance Commute ((,) x) ((,) y) where 
    commute (y, (x, a)) = (x, (y, a)) 

instance Commute ((->) x) ((,) y) where 
    commute (y, xa) = \x -> (y, xa x) 

-- instance Commute ((,) x) ((->) y) does not exist; try to write yourself! 
-- 
-- OR: 
-- It turns out that you need to somehow "travel back in time" to make it 
-- work... 
-- 
-- instance Commute ((,) x) ((->) y) where 
-- commute yxa = (..., \y -> let (x, a) = yxa y in a) 
+5

'Traversable' tipi sınıf sağladığından, muhtemelen bundan bahsetmeye değer. Bu tür imzası olan "dizi" işlevi: 'dizi :: Monad m => t (ma) -> m (ta)', 'NestedMonads' türünü NestedMonads mt = olarak tanımlayabilirsiniz (Monad m, Traversable t , Monad t) 've sonra bibind :: NestedMonads mt => m (ta) -> (a -> m (tb)) -> m (tb)'. [Kod] (http://lpaste.net/130180). – user3237465

+0

Bu, “İşe giriş” in ne yapabileceğine dair iyi bir sezgiler sunuyor, teşekkürler! –

+1

Burada, "Traversable" öğesinin iç monad üzerinde doğru kısıtlama olarak görünmediğini eklemek yararlı olabilir. Cevap bakın [burada] (http://stackoverflow.com/a/42298943/2684007). –