Başvuranlar beste yapar, monadlar yapmaz


110

Başvuranlar beste yapar, monadlar yapmaz.

Yukarıdaki ifade ne anlama geliyor? Ve ne zaman biri diğerine tercih edilir?


5
Bu ifadeyi nereden aldın? Bazı bağlamları görmek faydalı olabilir.
Fuz

@FUZxxl: Son zamanlarda twitter'daki debasishg'den birçok farklı insandan defalarca duydum.
missingfaktor

3
@stephen tetley: Bu tür birçok Applicatives'nin aslında bütün bir s ailesi olduğuna dikkat edin Monad, yani mümkün olan her "şekil" için bir tane. ZipLista değildir Monad, ancak ZipListuzunlukları sabittir. Reader"yapının" boyutunun ortam türünün temel niteliği olarak sabitlendiği uygun bir özel (veya genel mi?) durumdur.
CA McCann

3
@CAMcCann Şekli Readerizomorfizme kadar bir monad olacak şekilde düzeltirseniz, tüm bu fermuarlı uygulamalar (ister keser ister pedler) monadlarla kısıtlanır . Bir kabın şeklini düzelttiğinizde, bir işlevi bir memo trie gibi konumlardan etkili bir şekilde kodlar. Peter Hancock, logaritma kanunlarına uydukları için bu tür fonksiyonculara "Naperian" diyor.
pigworker

4
@stephen tetley: Diğer örnekler arasında sabit-monoid uygulama (monadların bir bileşimidir, ancak bir monad değildir) ve birim geciktirme uygulaması (katılmayı kabul etmemesi daha iyi olan) içerir.
pigworker

Yanıtlar:


115

Türleri karşılaştırırsak

(<*>) :: Applicative a => a (s -> t) -> a s -> a t
(>>=) :: Monad m =>       m s -> (s -> m t) -> m t

iki kavramı ayıran şeyin ne olduğuna dair bir ipucu elde ederiz. Bu (s -> m t)tipinde (>>=)bir değer gösterdiği sbir hesaplama davranışını belirler m t. Monadlar, değer ve hesaplama katmanları arasında girişime izin verir. (<*>)Operatör böyle müdahaleyi sağlar: fonksiyonu ve argüman hesaplamaları değerlere bağlı olmayan. Bu gerçekten ısırıyor. Karşılaştırmak

miffy :: Monad m => m Bool -> m x -> m x -> m x
miffy mb mt mf = do
  b <- mb
  if b then mt else mf

iki hesaplama arasında karar vermek için bazı etkilerin sonucunu kullanır (örneğin füzeleri fırlatmak ve bir ateşkes imzalamak), oysa

iffy :: Applicative a => a Bool -> a x -> a x -> a x
iffy ab at af = pure cond <*> ab <*> at <*> af where
  cond b t f = if b then t else f

ki değerini kullanır abarasında seçim yapmak için değerleri iki hesaplamalar atve afbelki de trajik etkisine, hem de elde edildiğini.

Monadik versiyon, esasen (>>=)bir değerden bir hesaplama seçmenin ekstra gücüne dayanır ve bu önemli olabilir. Bununla birlikte, bu gücü desteklemek, monadların bestelenmesini zorlaştırır. 'Double-bind' oluşturmaya çalışırsak

(>>>>==) :: (Monad m, Monad n) => m (n s) -> (s -> m (n t)) -> m (n t)
mns >>>>== f = mns >>-{-m-} \ ns -> let nmnt = ns >>= (return . f) in ???

bu kadar uzağa gidiyoruz, ama şimdi katmanlarımız karmakarışık. Elimizde bir tane var n (m (n t)), bu yüzden dışardan kurtulmamız gerekiyor n. Alexandre C'nin dediği gibi, uygun bir

swap :: n (m t) -> m (n t)

sırasını değiştirmek amacıyla niçeriye ve joindiğer bunu n.

Daha zayıf 'çift uygulama' tanımlaması çok daha kolaydır

(<<**>>) :: (Applicative a, Applicative b) => a (b (s -> t)) -> a (b s) -> a (b t)
abf <<**>> abs = pure (<*>) <*> abf <*> abs

çünkü katmanlar arasında hiçbir girişim yoktur.

Buna karşılık, Monads'nin ekstra gücüne gerçekten ne zaman ihtiyacınız olduğunu ve Applicativedestekleyen katı hesaplama yapısından ne zaman kurtulabileceğinizi anlamak iyidir .

Bu arada, monadları oluşturmak zor olsa da, ihtiyacınız olandan daha fazlası olabileceğini unutmayın. Türü m (n v)ile işlem gösterir mdaha sonra işlem, -Etkileri na -Etkileri v-değeri, mdaha önce -Etkileri bitiş n-Etkileri (dolayısıyla gerek başlangıç swap). m-Efektleri -etkileri ile birleştirmek istiyorsanız n, o zaman kompozisyon sorulamayacak kadar fazla olabilir!


3
Şüpheli örnek için, "belki de trajik bir etki için, her ikisini de gerçekleştirdikten sonra, iki hesaplamanın değerleri arasında seçim yapmak için ab değerini kullandığını" belirtirsiniz. Haskell'in tembel doğası sizi buna karşı korumaz mı? List = (\ btf -> eğer b ise t else f): [] varsa ve sonra şu ifadeyi yürütün: list <*> pure True <*> pure "merhaba" <*> pure (hata "kötü"). ... "Merhaba" alıyorum ve hata asla oluşmuyor. Bu kod neredeyse bir monad kadar güvenli veya kontrollü değil, ancak gönderi, başvuru sahiplerinin katı değerlendirmeye neden olduğunu öne sürüyor gibi görünüyor. Yine de genel olarak harika bir gönderi! Teşekkürler!
shj

7
Hala her ikisinin de etkilerini alıyorsunuz , ancak saf (hata "kötü") hiçbir etkiye sahip değil. Öte yandan, iffy (saf Doğru) (saf "merhaba") (hata "kötü") denerseniz, miffy'nin kaçındığı bir hata alırsınız. Ayrıca, iffy (saf Doğru) (saf 0) [1,2] gibi bir şey denerseniz, [0] yerine [0,0] elde edersiniz. Başvuranlar, sabit hesaplama dizileri oluşturdukları için bir tür katılığa sahiptirler, ancak bu hesaplamalardan kaynaklanan değerler , sizin gözlemlediğiniz gibi, yine de tembel bir şekilde birleştirilir.
domuz işçisi

Herhangi bir monad için mve nher zaman bir monad transformatör yazıp kullanarak mtçalışabileceğiniz doğru mu? Yani her zaman monadlar oluşturabilirsiniz, sadece transformatör kullanarak daha karmaşık mı? n (m t)mt n t
ron

4
Bu tür transformatörler genellikle mevcuttur, ancak bildiğim kadarıyla bunları oluşturmanın kanonik bir yolu yoktur. Farklı monadlardan araya eklenen etkilerin nasıl çözüleceğine dair gerçek bir seçim vardır, klasik örnek istisnalar ve durumdur. Bir istisna geri alma durumu değişmeli mi, değiştirilmemeli mi? Her iki seçeneğin de yeri var. Bunu söyledikten sonra, "keyfi serpiştirmeyi" ifade eden bir "özgür monad" şey var. data Free f x = Ret x | Do (f (Free f x)), sonra data (:+:) f g x = Inl (f x) | Tnr (g x)ve düşünün Free (m :+: n). Bu, aralamanın nasıl çalıştırılacağına ilişkin seçimi geciktirir.
pigworker

@pigworker Tembel / katı tartışma ile ilgili. Bence uygulamalarda etkiyi hesaplamanın içinden kontrol edemezsiniz , ancak etki katmanı daha sonraki değerleri değerlendirmemeye çok iyi karar verebilir. (Uygulayıcı) ayrıştırıcılar için bu, ayrıştırıcı erken başarısız olursa, sonraki ayrıştırıcıların değerlendirilmeyeceği / girdiye uygulanmayacağı anlamına gelir. İçin Maybebu aracı erken olduğunu Nothingdeğerlendirmesini bastırır asonraki bir / müteakip arasında Just a. Bu doğru mu?
ziggystar

75

Başvuranlar beste yapar, monadlar yapmaz.

Monads yapmak oluşturma, ancak sonuç bir monad olmayabilir. Aksine, iki başvurunun bileşimi zorunlu olarak bir başvurudur. Orijinal açıklamanın amacının "Uygulanabilirlik oluşturur, monadlık oluşturmaz" olduğundan şüpheleniyorum. Yeniden ifade edildi, " Applicativekompozisyon altında kapalı ve kapalı Monaddeğil."


24
Ek olarak, herhangi iki uygulama tamamen mekanik bir yolla oluşurken, iki monadın bileşimi ile oluşturulan monad bu bileşime özgüdür.
Apocalisp

12
Dahası, monadlar başka şekillerde oluştururlar, iki monadın ürünü bir monaddır, yalnızca bir tür dağıtım yasasına ihtiyaç duyan ortak ürünlerdir.
Edward KMETT

@Apocalisp, yorum dahil, bu en iyi ve en kısa cevaptır.
Paul Draper

39

Başvuru sahipleriniz varsa A1ve A2tür data A3 a = A3 (A1 (A2 a))de geçerlidir (böyle bir örneği genel bir şekilde yazabilirsiniz).

Öte yandan, eğer monadlarınız varsa M1ve M2o zaman tipin data M3 a = M3 (M1 (M2 a))mutlaka bir monad olması gerekmez ( kompozisyon için >>=veya joinkompozisyon için mantıklı genel bir uygulama yoktur ).

Bir örnek türü olabilir [Int -> a](burada bir tür yapıcı oluşturmak []ile (->) Intmonads her ikisi de). Kolayca yazabilirsin

app :: [Int -> (a -> b)] -> [Int -> a] -> [Int -> b]
app f x = (<*>) <$> f <*> x

Ve bu, herhangi bir uygulamaya genelleşir:

app :: (Applicative f, Applicative f1) => f (f1 (a -> b)) -> f (f1 a) -> f (f1 b)

Ama mantıklı bir tanımı yok

join :: [Int -> [Int -> a]] -> [Int -> a]

Buna ikna olmadıysanız, şu ifadeyi düşünün:

join [\x -> replicate x (const ())]

Döndürülen listenin uzunluğu, bir tam sayı sağlanmadan önce sabit olarak ayarlanmalıdır, ancak doğru uzunluğu sağlanan tam sayıya bağlıdır. Bu nedenle, joinbu tür için doğru bir işlev olamaz.


1
... o halde bir işlevin işe yarayacağı zaman monadlardan kaçının?
andrew cooke

2
@andrew, functor demek istediyseniz, o zaman evet, functors daha basittir ve yeterli olduğunda kullanılmalıdır. Her zaman olmadığını unutmayın. Örneğin IO, bir olmadan Monadprogramlamak çok zor olurdu. :)
Rotsor

17

Maalesef, asıl amacımız olan monadların bileşimi oldukça zor. Aslında, belli bir anlamda, sadece iki monadın işlemlerini kullanarak yukarıdaki tipte bir birleştirme işlevi oluşturmanın bir yolu olmadığını gerçekten kanıtlayabiliriz (ispatın ana hatları için eke bakın). Bir kompozisyon oluşturmayı ummamızın tek yolu, iki bileşeni birbirine bağlayan bazı ek yapılar olmasıdır.

Monadları oluşturma, http://web.cecs.pdx.edu/~mpj/pubs/RR-1004.pdf


4
Tl; dr için sabırsız okuyucular: eğer (f?) Doğal bir dönüşüm sağlayabilirseniz monadlar oluşturabilirsinizswap : N M a -> M N a
Alexandre C.

@Alexandre C .: Sadece "eğer", sanırım. Tüm monad transformatörler, doğrudan functor bileşimi ile tanımlanmamıştır. Örneğin ContT r m a, ne öyle ne m (Cont r a)de Cont r (m a), StateT s m akabaca öyle Reader s (m (Writer s a)).
CA McCann

@CA McCann: (M monad, N monad, MN monad, NM monad) 'dan (takas var: MN -> NM doğal)' a ulaşamıyorum. Öyleyse şimdilik "eğer" e bağlı kalalım (belki de cevap gazetede, hızlıca baktığımı itiraf etmeliyim)
Alexandre C.

1
@Alexandre C .: Kompozisyonların monadlar olduğunu belirtmek zaten yeterli olmayabilir - ayrıca iki parçayı bütünle ilişkilendirmek için bir yola ihtiyacınız var. Varlığı swap, kompozisyonun ikisinin bir şekilde "işbirliği yapmasına" izin verdiğini ima eder. Ayrıca, sequencebazı monadlar için özel bir "takas" durumu olduğunu unutmayın . Yani bir flipaslında.
CA McCann

7
Yazmak swap :: N (M x) -> M (N x)için bana returns(uygun şekilde fmapped) kullanarak Mön tarafa ve Narkaya bir yerleştirebilir, buradan gidebilir ve N (M x) -> M (N (M (N x)))ardından joinkompozitin M (N x).
pigworker

7

Dağılım yasası çözümü l: MN -> NM yeterlidir

NM'nin monadisitesini garanti etmek için. Bunu görmek için bir birime ve bir multiye ihtiyacınız var. çokluya odaklanacağım (birim birim_ birimdir)

NMNM - l -> NNMM - mult_N mult_M -> NM

Bu mu değil MN bir monad olduğunu garanti.

Ancak önemli gözlem, dağıtım hukuku çözümlerine sahip olduğunuzda devreye girer.

l1 : ML -> LM
l2 : NL -> LN
l3 : NM -> MN

bu nedenle LM, LN ve MN monadlardır. Soru, LMN'nin bir monad olup olmadığı (ya da

(MN) L -> L (MN) veya N (LM) -> (LM) N ile

Bu haritaları yapmak için yeterli yapıya sahibiz. Bununla birlikte, Eugenia Cheng'in gözlemlediği gibi , her iki yapının monadisitesini garanti etmek için altıgen bir koşula (Yang-Baxter denkleminin bir sunumuna karşılık gelen) ihtiyacımız var. Aslında, altıgen koşulda, iki farklı monad çakışır.


9
Bu muhtemelen harika bir cevap, ama kafamın çok ötesine geçti .
Dan Burton

1
Bunun nedeni, Applicative ve haskell etiketi terimini kullanarak, bu haskell ile ilgili bir sorudur, ancak yanıtı farklı bir gösterimle.
codhot
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.