2014-09-24 83 views
19

我瞭解<$>的簽名背後的原因,因爲它只是fmap的中綴版本,但將其與>>=的簽名類型進行比較,它對我來說意義不大。

我們先來確定一下我的意思。

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

縱觀類型簽名,我們可以看到,>>=需要在左邊的值,並在右邊,這讓很多的,如果你考慮它的鏈接財產意義上的功能:foo >>= bar >>= baz

哪讓我想知道,爲什麼<*><$>也這樣做?你不能寫foo <*> bar <*> baz,因爲它需要foo <*> bar的輸出是一個函數,而不是一個值。

我知道<**>=<<存在兩個翻轉參數的順序,讓我做這樣的事情:

Just 4 <**> pure (+3) <**> pure (*2) >>= (\x -> Just (x-3)) 

這本來是精美減少到:

Just 4 <$$> (+3) <$$> (*2) >>= (\x -> Just (x-3)) 

如果<$$>已經存在,或者如果參數順序<$><*>已被顛倒過來。

另一件讓我想知道爲什麼這種差異存在的原因是它讓新手很難習慣和/或記住它是功能還是先看到的價值,而不必去查找它。

那麼,爲什麼在中<*><$>的情況下,它是fn op val>>=它的val op fn

+4

但你經常需要的正是這種與'Applicatives' :例如'(+)<$>只是5 <*>只是5 '< - 不是'只是'美麗;) – Carsten 2014-09-24 11:27:58

+2

'(>> =)'構造鏈條「勢在必行的風格」(否則我們會留下奇怪的原因它不是與'$'相同的順序)。 '(<= <)'也允許鏈接 - 「'(。)'style」。 '(<*>)'對於「應用」多參數函數很有用 - 按照聲明的順序提供參數。 – 2014-09-24 11:31:59

+2

真正的問題是爲什麼'(>> =)'使用與Haskell中幾乎所有對象相反的順序。 (= <<):: Monad m =>(a - > mb) - >(ma - > mb)'的類型看起來更像Haskell中的所有其他部分,而非'(>> =)= flip = <<)'。 – 2014-09-25 09:51:40

回答

18

答案是「爲什麼它以這個順序參數」基本上是「因爲」。定義這些功能的人認爲這是最好的方法。 (在每種情況下可能都不是同一個人)。但是,我會提供一些示例:

假設我們有某種解析單子。再假設我們已經定義

data Foobar = Foobar X Y Z 

parseFoo :: Parser X 
parseBar :: Parser Y 
parseBaz :: Parser Z 

然後,我們可以寫

parseFoobar :: Parser Foobar 
parseFoobar = do 
    foo <- parseFoo 
    bar <- parseBar 
    baz <- parseBaz 
    return (Foobar foo bar baz) 

或者明確,

parseFoobar = 
    parseFoo >>= \ foo -> 
    parseBar >>= \ bar -> 
    parseBaz >>= \ baz -> 
    return (Foobar foo bar baz) 

現在讓我們來編寫應用性風格:

parseFoobar = return Foobar <*> parseFoo <*> parseBar <*> parseBaz 

或者可選地,

parseFoobar = Foobar <$> parseFoo <*> parseBar <*> parseBaz 

如果我們假設<**> = flip <*>存在(並具有正確的關聯性),那麼我們有

parseFoobar = parseBaz <**> parseBar <**> parseFoo <**> return Foobar 

這只是看起來很奇怪。在最後的函數中,參數的順序是相反的?你爲什麼要這樣寫呢? (請注意,任何效果都是也是,相反順序)。

在monadic版本中,效果發生在頂部到底部。在應用版本中,效果從左到右發生。這似乎是「自然的」。

+0

你可以在示例函數中添加一些示例類型嗎?只是讓我有一個更容易的時間圍繞發生的事情 – 2014-09-24 11:45:12

+0

編輯了一點東西。 – MathematicalOrchid 2014-09-24 11:55:12

+0

這是如何工作的功能鏈方面呢?你在這裏做的是將一個函數應用到3個包裝的值,但是「僅僅4 >> = foo >> = bar >> = baz',與說'+(+)<$>只有3 <*>只有4' – 2014-09-24 12:21:57

22

不要讓monad阻礙我們前進。想想應用程序。

比較:

(<$>) :: Functor f => (a -> b) -> f a -> f b 

($) :: (a -> b) -> a -> b 

你可以看到有一個仿函數在普通的應用程序和應用程序之間的連接。

花絮:有過proposals使用括號超載空白(應用程序),以便我們可以這樣寫:

(| f a1 .. an |) 

,而不是

pure f <*> a1 <*> .. <*> an 
+4

我從來沒有見過這種情況,我只用'fmap'來看'<$>',現在你已經把它作爲'$'的一個變體呈現出來了,它更有意義 – 2014-09-24 12:35:41

+1

我認爲這應該是可接受的回答! – 2014-10-03 21:45:09

相關問題