我已經了解了 functor 和 monad 的定義,但是除了定義之外,我仍然無法弄清楚它們之間的區別。我已經閱讀了這個問題的一些答案,在Functor 和 Monad 之間有什么區別?一條評論說
函子采用純函式(和函子值),而單子采用 Kleisli 箭頭,即回傳單子(和單子值)的函式。因此,您可以鏈接兩個 monad,而第二個 monad 可以取決于前一個 monad 的結果。你不能用函子做到這一點。
這條評論很有趣,讓我了解了他們的不同之處。但我還有一些問題。
- 為什么函子不能使用前一個的結果?因為
fmap :: (a -> b) -> f a -> f b,當我使用純函式進行柯里化時fmap,我可以獲得一個f a -> f b函式,這f b取決于,結果f a是否意味著函子內的資料? - 在范疇論中我可以理解評論,因為我無法獲得范疇論中的元素,但在 Haskell 中我發現我可以使用仿函式的結果,因為 Haskell 可以記住資料建構式,Haskell 是否會阻止我理解這條評論?我應該在純范疇論中理解這一點嗎?
uj5u.com熱心網友回復:
使用fmap :: Functor f => (a -> b) -> f a -> f b時,引數函式永遠不能依賴于仿函式f本身的任何結構;它怎么能,當它被傳遞的只是a值,與它無關f?當你fmap用來獲取一個f a -> f b函式時,負責構建最終f b值的代碼是fmap它自己的代碼,而不是a -> b被映射的函式的代碼。被映射的函式甚至可能根本不會被呼叫(例如,如果您使用Maybe仿函式并且f a值為Nothing,則 for 內部沒有a值fmap可以傳遞給a -> b函式,并且永遠不會被呼叫)。
But在andfmap中是完全多型的,這意味著 的代碼不知道這些型別是什么,并且不能對它們進行任何假設。因此必須以僅依賴于;添加的函子結構的方式構建最終值。的代碼不能查看任何值并決定要做什么。事實上,合法的唯一能做的就是回傳一個與輸入值具有完全相同結構的值,只是其中的任何s 都被函式回傳的值替換。這就是為什么您可以使用任何資料型別的原因;至多有一種方法可以使任何給定的資料型別成為abfmapfmapf bffmapafmapf aaba -> bderiving FunctorFunctor,編譯器可以為你做這件事。
但是 monad=<<函式1有這種型別:Monad m => (a -> m b) -> m a -> m b. 這里映射的函式有 type a -> m b。這意味著最后m b回傳的最終結構并不完全由=<<(至少不一定)的代碼決定。該a -> m b函式知道正在使用什么 monad,并在其m b值中回傳一些 monadic 結構,而不僅僅是 raw b。映射函式仍然沒有接收任何一元結構,因此它的中間m b結果不能依賴于它;最終m b值對原始單子結構的任何依賴m a都必須由代碼確定=<<(同樣,不能根據a值本身)。但是映射函式(如果被呼叫)確實會貢獻一些單子結構,這可能取決于a值(因為映射函式不必是完全多型的a;它可以檢查a值并根據它們做出決策)。
這意味著最終輸出的任何m b部分最終都可能取決于輸入的任何部分(如果我們不知道映射了什么函式,或者=<<函式內部如何作業)。這與仿函式的情況完全不同,即使對特定仿函式或映射函式一無所知,我們也知道最終輸出將始終看起來像“將as 替換為bs 的輸入副本”(特別是b替換每個a由映射函式確定)。
可能值得注意的是:我在這里所說的幾乎所有內容都是這些型別含義的直接結果:
Functor f => (a -> b) -> f a -> f b
Monad m => (a -> m a) -> m a -> m b
這些不是你必須記住的函子和單子的特殊屬性,它只是那些具有這些型別的操作的結果。(函子和單子定律表達了型別不表達的東西,但我不需要呼叫它們來解釋這種差異)
一旦你真正習慣了以 Haskell 方式思考型別,你就可以通過查看型別自己弄清楚這一點。我當然沒有;我在學習的時候給我解釋了(十幾次),從那時起我就記住了。但是現在我可以在沒有任何教程或解釋的情況下弄清楚我以前從未見過的新 API 的類似情況。
1我使用反向=<<運算子而不是傳統運算子,僅僅是因為它與/運算子>>=更好地對齊。fmap<$>
uj5u.com熱心網友回復:
monad是一個仿函式,或者更確切地說是一個仿函式以及兩個額外的操作。(monad 的原始(?)名稱是三元組。)
查看原始的數學定義會有所幫助,它提供了一個操作unit :: Monad m => a -> m a,而不是(>>=) :: Monad m => m a -> (a -> m b) -> m b定義了一個名為 的操作join :: Monad m => m (m a) -> m a,它可以讓您“移除”一層提升。這兩個公式是等價的,join可以定義為(>>=)
join ma = ma >>= id
反之亦然:
ma >>= f = join $ fmap f ma
最后一個定義對 monad 給出了不同的解釋:它是一個可以“壓縮”的函子;通過將一個函式映射到一個已經提升的值上,然后將兩個提升層組合回一個層,鏈接是免費的。
unit在任何一種情況下都是必需的,因為首先沒有辦法通過 just 來獲得提升的價值fmap。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/528526.html
