2016-02-14 27 views
8

Fonksiyonel programlamada yeni (javascript'ten geliyor), ve ikisi arasındaki farkı anlatan zor bir zaman geçiriyorum, ki bu da aynı zamanda monoperler ve funktörleri anlayabiliyorum.Fmap ile bağlama arasındaki fark nedir?

Funktör: (basitleştirilmiş)

class Functor f where 
    fmap :: (a -> b) -> f a -> f b 

Monad:

class Monad m where 
    (>>=) :: m a -> (a -> m b) -> m b 
  • fmap bir işlev ve functor alır ve bir functor döndürür.
  • >>= bir işlev ve bir monad alır ve bir monad döndürür.

ikisi arasındaki fark fonksiyonu parametrede ise:

  • fmap - (a -> b)
  • >>=-(a -> m b)

>>= bir monad veren bir işlev parametresini alır. Bunun önemli olduğunu biliyorum, ancak bu küçük şeylerin monadları nasıl eğlenceli hale getirdiğini görmek zor. Birisi açıklayabilir mi?

+4

bu daha kolay (>> =) ', [' (= <<) '] (https://stackoverflow.com/questions/34545818/is-monad-bind-operator- 'arasında ters çevrilmiş versiyonunu görülür Daha yakın için fonksiyonlu-bileşiminin zincirleme-ya da fonksiyonel/34561605 # 34561605). '(G <$>) :: f a -> f b' işleviyle, g: a -> b' işlevinin' f' 'sarması üzerinde hiçbir etkisi yoktur - değiştirmez. '(K = <<) :: m a -> mb' ile,' k 'a-> mb' işlevinin kendisi * yeni' m' sarmayı '' yaratan '' oluşturur, böylece değişebilir –

+0

@WillNess Bunu anlayabilirim, ama ben Bunu göremiyorum, bence asıl sorun şu ki, ne yapabildiğimi göremiyorum. >> '' '' fmap'' yapamaz yapamıyorum. fmap'in yetersiz olduğunu gösteren bir örnek: – m0meni

+4

listelere gidiyor, bir listeden bazı öğeleri filtrelemeye çalışıyorum, 'map' kullanarak. yapamazsınız, ancak concatMap' ile şunları yapabilirsiniz:' map (\ x- > x + 1) [1,2,3] 'vs concatMap (\ x-> [x, x + 1 | çift x]) [1,2,3])'. –

cevap

13

Eh, (<$>)fmap için bir takma ad ve takas argümanlarla (>>=) aynıdır:

(<$>) :: (x -> y) -> b x -> b y 
(=<<) :: (x -> b y) -> b x -> b y 

fark artık oldukça net: bağlama fonksiyonu ile, biz döndüren bir işlev uygulamak y yerine b y. Peki bu ne fark eder? 3 için foo uygulamak ve bir Just geri sonucunu koyacağız

foo <$> Just 3 

Bildirim (<$>) o:

bu küçük örneği ele alalım. Başka bir deyişle, bu hesaplama 'un sonucu Nothing olamaz. Aksine:

bar =<< Just 3 

Bu hesaplama Nothing dönebilirsiniz. (Örneğin, bar x = Nothing yapacağız.)

Biz liste monad ile benzer bir şey yapabilirsiniz: Kısacası

foo <$> [Red, Yellow, Blue] -- Result is guaranteed to be a 3-element list. 
bar =<< [Red, Yellow, Blue] -- Result can be ANY size. 

, sonucun (<$>) (yani fmap), "yapı" ile her zaman giriş ile aynıdır.Ancak (yani (>>=)) ile sonuç yapısı değişebilir. Bu, koşullu yürütmeye, girdiye tepki vermeye ve bir çok başka şeye izin verir.

+4

sadece tamlığı için, Uygulayıcı '' Nothing' de geri dönebilir: 'Hiçbir şey <*> Sadece 3'. Fark, "borulama" (yani hesaplama yapısı), hesaplandığında * sabittir *, * önce * o * "çalışır" *. Ancak Monad'lar ile borulama üretilen değerlere bağlı olarak değişebilir * iken "çalışır". (IO durumunda, '3' muhtemelen kullanıcı girdisi olarak alınır). - * liste * örneği esp. burada iyi: '(foo <$>)' yapıyı (liste uzunluğu) tutar; '([baz, quux] <*>)' yapıyı * değiştirecektir * (uzunluk-6 listesi oluştur); Monad ile tüm bahisler kapalı. –

8

Kısa bir cevabı m (m a)'u m a'a dönüştürebilirsiniz ki bu bir anlam ifade edecek şekilde bir Monad'dir. Bu, tüm Monad'lar için mümkündür ancak Functor'lar için gerekli değildir.

Ben en kafa karıştırıcı şey functors ortak örneklerle (örneğin List, Maybe, IO) hepsi de monad'ların olduğunu düşünüyorum. Bir Funtor olan ama Monad olmayan bir şeyin örneğine ihtiyacımız var.

Örnek bir takvim programından örnek kullanacağım. Aşağıdaki kod, olayla birlikte gelen bazı verileri ve gerçekleştiği zamanı kaydeden bir Event Functor öğesini tanımlar.

import Data.Time.LocalTime 

data Event a = MkEvent LocalTime a 

instance Functor Event where 
    fmap f (MkEvent time a) = MkEvent time (f a) 

Event nesne depolar olay meydana süresi ve fmap kullanılarak değiştirilebilir bazı ekstra verileri. Biz iki LocalTime nesneler ile sona erecek çünkü bulmak

instance Monad Event where 
    (>>=) (MkEvent timeA a) f = let (MkEvent timeB b) = f a in 
           MkEvent <notSureWhatToPutHere> b 

: Artık bir Monad denemek ve bunu yapmanıza olanak sağlar. timeA verilen Event ve timeB tarafından f a sonucu verilen verilen. Bizim Event türünde bir LocalTime (time) oluşur ve bu nedenle bir Monad iki LocalTime s bir tane dönmeden mümkün değildir yapmak olarak tanımlanmıştır. (Bunu yapmak mantıklı olabilecek bazı durumlar olabilir ve böylece bunu gerçekten istiyorsan bunu bir monaya dönüştürebilirsin).

+3

Bir monad olmayan klasik/ortak bir functor örneği "newtype Const a b = Const a". – dfeuer

+3

'salt x >> = f' bir monad yasasının' f x' olması için gereklidir, ancak 'pure :: b -> Const a b' argümanını kullanamaz. – dfeuer

+1

@dfeuer Bu dikişler [basit olmak için çok basit] (https://ncatlab.org/nlab/show/too+simple+to+be+simple). Ayrıca fmap f (Const x) = Const x' – HEGX64

3

IO'un bir Functor olduğu ve Monad değil bir an için varsayalım. İki eylemi nasıl sıralayabiliriz? getChar :: IO Char ve putChar :: Char -> IO() gibi deyin. getChar üzerinden eşlemeyi deneyebiliriz (çalıştırıldığında, stdin'den putChar kullanarak).

fmap putChar getChar :: IO (IO()) 

Şimdi, çalıştırıldığında, stdin'den bir Char okur ve, çalıştırıldığında, Stdout'a Char yazar bir program üreten bir program var. Ama aslında istediğimiz, çalıştırıldığında, stdin'den Char'u okur ve Char'u stdout'a yazar. tek başına

join :: IO (IO()) -> IO() 

Functor bu işlevi sağlamaz: Bu yüzden (IO durumda, "dizilim") "düzleşme" türündeki fonksiyon gerekir.

join :: Monad m => m (m a) -> m a 

Ne tüm bu var >>= ile ilgisi: Ama buna daha genel türü olan Monad bir fonksiyonu vardır? O sırada da, monadic bağlama fmap ve join sadece bir kombinasyonudur:

:t \m f -> join (fmap f m) 
(Monad m) => m a1 -> (a1 -> m a) -> m a 

farkı görmenin bir başka yolu fmap asla eşlenen değerin genel yapısını değiştirir, ancak join (ve dolayısıyla >>= yanı) olmasıdır bunu yapabilir. IO fiiller açısından

, irade fmap asla neden addicional/yazar veya başka etkileri okur. Ancak join, dış eylemin ardından iç hareketin okuma/yazmalarını sıralar.

İlgili konular