亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

Monad用簡單的英語?(對于沒有FP背景的OOP程序員)

Monad用簡單的英語?(對于沒有FP背景的OOP程序員)

用OOP程序員會理解的術語(沒有任何函數式編程背景),什么是monad?它解決了什么問題,最常使用的地方是什么?編輯:為了闡明我正在尋找的理解類型,假設您正在將具有monad的FP應用程序轉換為OOP應用程序。您將如何將monad的責任移植到OOP應用程序?
查看完整描述

3 回答

?
四季花海

TA貢獻1811條經驗 獲得超5個贊

更新:這個問題是一個非常長的博客系列的主題,您可以在Monads上閱讀它 -感謝您提出的偉大問題!


用OOP程序員會理解的術語(沒有任何函數式編程背景),什么是monad?


monad是一種類型的“放大器”,它遵循某些規則并提供某些操作。


首先,什么是“類型放大器”?我的意思是說,有一個系統可以讓您選擇一種類型并將其轉換為更特殊的類型。例如,在C#中考慮Nullable<T>。這是一種放大器。它使您可以使用一個類型,例如int,并為該類型添加新功能,即現在可以在以前無法使用時為null。


作為第二個示例,請考慮IEnumerable<T>。它是一種類型的放大器。它允許您采用一個類型,例如,string并為該類型添加新功能,即,您現在可以從任意數量的單個字符串中構成一個字符串序列。


什么是“某些規則”?簡而言之,存在一種合理的方式,使基礎類型上的功能在放大類型上起作用,從而使它們遵循功能組成的正常規則。例如,如果您有一個整數函數,請說


int M(int x) { return x + N(x * 2); }

則相應的on函數Nullable<int>可以使所有運算符和其中的調用與以前一樣“以相同的方式”協同工作。


(這是非常模糊和不精確的;您要求提供一種解釋,該解釋沒有假定任何有關功能組成的知識。)


什么是“操作”?


有一個“單位”操作(有時也稱為“返回”操作),該操作從普通類型獲取值并創建等效的單價值。本質上,這提供了一種獲取未放大類型的值并將其轉換為放大類型的值的方法??梢詫⑵鋵崿F為OO語言的構造函數。


有一個“綁定”操作,它接受一個單子值和一個可以轉換該值并返回新單子值的函數。綁定是定義monad語義的關鍵操作。它使我們可以將未放大類型的操作轉換為對放大類型的操作,這要遵循前面提到的功能組成規則。


通常有一種方法可以使未放大類型從放大類型中退回。嚴格來說,此操作不需要具有monad。(盡管如果您想擁有自己的名字是很有必要的。我們將不在本文中進一步討論。)


再以Nullable<T>一個例子為例。您可以使用構造函數將int轉換為Nullable<int>。C#編譯器會為您處理大多數可為空的“提升”,但是如果沒有,提升轉換將非常簡單:例如,


int M(int x) { whatever }

變成


Nullable<int> M(Nullable<int> x) 

    if (x == null) 

        return null; 

    else 

        return new Nullable<int>(whatever);

}

并通過該屬性將其變Nullable<int>回原樣。intValue


函數轉換是關鍵。注意,在轉換中如何捕獲可為空操作的實際語義,即可null傳播操作的語義null。我們可以對此進行概括。


假設您具有從int到的功能int,就像我們原來的功能一樣M。您可以輕松地將其轉換為接受an int并返回a 的函數,Nullable<int>因為您可以通過可為null的構造函數運行結果。現在假設您具有以下高階方法:


static Nullable<T> Bind<T>(Nullable<T> amplified, Func<T, Nullable<T>> func)

{

    if (amplified == null) 

        return null;

    else

        return func(amplified.Value);

}

看到你能做什么?現在,任何采用an int并返回an int或采用an int并返回a的Nullable<int>方法現在都可以應用可空語義。


此外:假設您有兩種方法


Nullable<int> X(int q) { ... }

Nullable<int> Y(int r) { ... }

而您想組成它們:


Nullable<int> Z(int s) { return X(Y(s)); }

即和Z的組成。但是您不能這樣做,因為需要一個,然后返回一個。但是,由于您具有“綁定”操作,因此可以進行以下工作:XYXintYNullable<int>


Nullable<int> Z(int s) { return Bind(Y(s), X); }

對單聲道的綁定操作使放大類型上的功能組合起作用。我上面揮揮手的“規則”是單子保留正常功能組成的規則。與標識函數組成的結果將產生原始函數,該組成具有關聯性,依此類推。


在C#中,“綁定”稱為“ SelectMany”??匆幌滤绾卧谛蛄衜onad上工作。我們需要做兩件事:將一個值轉換為一個序列,然后對序列進行綁定操作。作為獎勵,我們還具有“將序列變回值”的功能。這些操作是:


static IEnumerable<T> MakeSequence<T>(T item)

{

    yield return item;

}

// Extract a value

static T First<T>(IEnumerable<T> sequence)

{

    // let's just take the first one

    foreach(T item in sequence) return item; 

    throw new Exception("No first item");

}

// "Bind" is called "SelectMany"

static IEnumerable<T> SelectMany<T>(IEnumerable<T> seq, Func<T, IEnumerable<T>> func)

{

    foreach(T item in seq)

        foreach(T result in func(item))

            yield return result;            

}

可為空的monad規則是“將產生可為空的兩個函數組合在一起,檢查內部函數是否為null;如果滿足,則產生null,否則為null,然后用結果調用外部函數”。這是可為空的所需語義。


序列monad規則是“將產生序列的兩個函數組合在一起,將外部函數應用于內部函數產生的每個元素,然后將所有結果序列連接在一起”。Bind/ SelectMany方法捕獲了monad的基本語義;這是告訴您monad真正含義的方法。


我們可以做得更好。假設您有一個整數序列,以及一個采用整數并產生字符串序列的方法。我們可以對綁定操作進行一般化,以允許接受和返回不同放大類型的函數的組合,只要其中一個的輸入與另一個的輸出匹配即可:


static IEnumerable<U> SelectMany<T,U>(IEnumerable<T> seq, Func<T, IEnumerable<U>> func)

{

    foreach(T item in seq)

        foreach(U result in func(item))

            yield return result;            

}

因此,現在我們可以說:“將這組單個整數放大為整數序列。將該特定整數轉換為一串字符串,放大為一系列字符串。現在將這兩個操作放在一起:將這組整數放大為”所有的字符串序列。” Monads使您可以構成放大。


它解決了什么問題,最常使用的地方是什么?


這就好比問“單例模式能解決什么問題?”,但我會給它一個機會。


Monad通常用于解決以下問題:


我需要為此類型創建新功能,并且仍然要組合此類型上的舊功能以使用新功能。

我需要捕獲一堆關于類型的操作,并將這些操作表示為可組合的對象,構建越來越大的合成,直到我正確地表示了一系列操作,然后才需要從事情中得到結果。

我需要用一種討厭副作用的語言清晰地表示副作用操作

C#在其設計中使用了monad。如前所述,可為空的模式與“也許是單子”非常相似。LINQ完全由monad組成。該SelectMany方法是操作組合的語義工作。(Erik Meijer喜歡指出,每個LINQ函數實際上都可以由實現SelectMany;其他一切只是為了方便。)


為了闡明我正在尋找的理解類型,假設您正在將具有monad的FP應用程序轉換為OOP應用程序。您將如何將Monad的責任移植到OOP應用程序中?


大多數OOP語言沒有足夠豐富的類型系統來直接表示monad模式本身。您需要一個類型系統,該系統支持比通用類型更高類型的類型。所以我不會嘗試這樣做。相反,我將實現代表每個monad的泛型類型,并實現代表您需要的三個操作的方法:將值轉換為放大的值,(也許)將放大的值轉換為一個值,并將未放大的值的函數轉換為放大值的函數。


一個很好的起點是我們如何在C#中實現LINQ。研究SelectMany方法;這是理解序列monad如何在C#中工作的關鍵。這是一種非常簡單的方法,但功能非常強大!


建議進一步閱讀:


有關C#中monad的更深入和理論上合理的解釋,我強烈建議我(Eric Lippert的同事)Wes Dyer關于該主題的文章。本文是monads最終為我“點擊”時向我解釋的內容。

Monads的奇跡

一個很好的說明,為什么您可能想要一個monad (在示例中使用Haskell)。

您可能已經發明了Monad?。ㄒ苍S你已經擁有了。)丹·皮波尼

上一篇文章到JavaScript的“翻譯”。

James Coglan 所讀過的有關Monad 最佳入門的精選部分的從Haskell到JavaScript的翻譯


查看完整回答
反對 回復 2019-10-04
?
斯蒂芬大帝

TA貢獻1827條經驗 獲得超8個贊

為什么我們需要單子?

我們只想使用函數進行編程。(畢竟-FP是“功能性編程”)。

然后,我們有第一個大問題。這是一個程序:


f(x) = 2 * x


g(x,y) = x / y


我們如何說 要先執行什么?我們如何僅使用函數就可以形成一個有序的函數序列(即程序)?


解決方案:編寫函數。如果先要g然后f再寫就可以f(g(x,y))。好的但是 ...


更多問題:某些功能可能會失?。磄(2,0),除以0)。我們在FP沒有“例外”。我們該如何解決?


解決方案:讓函數讓函數返回兩種東西:g : Real,Real -> Real讓我們g : Real,Real -> Real | Nothing(函數從兩個實數轉換為(實數或虛無))而不是讓(函數從兩個實數轉換為實數)。


但是函數(為了簡化)應該只返回一件事。


解決方案:讓我們創建一種新的要返回的數據類型,即“ 裝箱類型 ”,其中可能包含實數或僅是空值。因此,我們可以擁有g : Real,Real -> Maybe Real。好的但是 ...


現在會發生什么f(g(x,y))?f還沒準備好消費Maybe Real。而且,我們不想更改可以連接的每個函數g來消耗Maybe Real。


解決方案:讓我們有一個特殊的功能來“連接” /“組合” /“鏈接”功能。這樣,我們可以在幕后調整一項功能的輸出以提供下一項功能。


在我們的例子中:(  g >>= f連接/組成g到f)。我們要>>=獲取g的輸出,對其進行檢查,以防萬一它Nothing不調用f并返回Nothing;或者相反,提取盒裝Real并f用它喂食。(此算法只是>>=針對該Maybe類型的實現)。


使用此相同的模式可以解決許多其他問題:1.使用“框”來整理/存儲不同的含義/值,并具有g返回這些“框值”的函數。2.讓作曲者/鏈接g >>= f者幫助將g的輸出連接到f的輸入,因此我們完全不需要更改f。


使用此技術可以解決的顯著問題是:


具有全局狀態,函數序列中的每個函數(“程序”)可以共享:solution StateMonad。


我們不喜歡“不純函數”:對于相同輸入產生不同輸出的函數。因此,讓我們標記這些函數,使它們返回標記/裝箱的值:monad。IO


完全幸福!


查看完整回答
反對 回復 2019-10-04
?
楊魅力

TA貢獻1811條經驗 獲得超6個贊

我會說與單子最接近的OO類比是“ 命令模式 ”。


在命令模式中,將普通語句或表達式包裝在命令對象中。該命令對象公開了一個執行方法,該方法執行包裝的語句。因此,語句變成了可以隨意傳遞和執行的一流對象??梢越M合命令,因此您可以通過鏈接和嵌套命令對象來創建程序對象。


這些命令由單獨的對象(調用程序)執行。使用命令模式(而不是僅執行一系列普通語句)的好處是,不同的調用者可以對應如何執行命令應用不同的邏輯。


命令模式可用于添加(或刪除)宿主語言不支持的語言功能。例如,在無例外的假設OO語言中,可以通過向命令公開“ try”和“ throw”方法來添加例外語義。當命令調用throw時,調用者將在命令列表(或樹)中回溯,直到最后一次“ try”調用為止。相反,您可以通過捕獲每個命令拋出的所有異常并將其轉換為錯誤代碼,然后將其傳遞給下一個命令,從某種語言中刪除異常語義(如果您認為異常不好)。


像這樣的更加花哨的執行語義,例如事務,非確定性執行或連續性,都可以用本機不支持的語言來實現。如果您考慮一下,這是一個非常強大的模式。


現在,實際上,命令模式并未像這樣被用作通用語言功能。將每個語句轉換為單獨的類的開銷將導致大量的樣板代碼。但是從原理上講,它可以用來解決與單子在fp中解決相同的問題。


查看完整回答
反對 回復 2019-10-04
  • 3 回答
  • 0 關注
  • 542 瀏覽

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號