--- title: Monad description: image: tags: not complete, programming robots: lang: zh-tw dir: breaks: GA: disqus: hackmd slideOptions: --- # Monad --- ## 在開始之前 不過這篇不會著重於Monad的應用與實作 而是解釋Monad 的理論,演進與我們目前接觸到的monad 以及 這是一篇引導 OOP or C# 開發者來認識Monad是什麼事情的文章 此文章僅需要熟悉OOP 與 匿名委派 (or Lambda) 即可繼續 ## 注意 閱讀此文章有可能會改變你的思考模式 以至於改變你的開發模式 畢竟這不是只有在我和我觀察到的現象而已 包含[參考連結](如何用c#解釋monad)中都提到 ![](https://i.imgur.com/4FkOugb.jpg) ``` 一旦有人了解了Monad是什麼以及如何使用Monad,他們就會失去向他人解釋它們的能力。 ``` 這是一件很有趣的事情,也許光憑這一篇文章還不能改變,但如果持續這樣下去也許真的會發生 後面會舉一個案例來說明這個現象 ## 開始吧 --- ### method(or function) , delegate, lambda 如果已經熟悉lambda是什麼 可以跳過此章節 #### Method 在OOP中方法通常都存在於類別之中 而且調用方法必須要指定其路徑 ```csharp public class SampleClass { public void Method() { Console.WriteLine("Hello World") } public static void Main(string[] args) { //必須要指定其路徑 SampleClass 下的 Method 才能執行 new SampleClass().Method(); } } ``` 為了print Hello World 得先宣告一個方法來執行他,在宣告一個類別用作實例化 是否能將事情變得更為簡單一點,以現在的C#來說當然是可以的 #### Delegate C#存在一種宣告叫做委派,委派是一個特殊型別名為 Delegate ```csharp //參考前例的 SampleClass.Method public delegate void DelegateMethod(); ``` #### MulticastDelegate and 匿名委派 接著出現了匿名委派 ```csharp delegate() { } ``` 匿名委派因為是於使用時 可以省去總是得要宣告的委派名稱的情況 所以匿名委派跟前述委派的宣告的差異為 是否具有實例 在匿名委派中通常都必須把實際的過程跟匿名委派寫在一起 而(具名)委派則是可同時持有多個委派實例 也就是 MulticastDelegate #### Lambda 而lambda 則是把匿名委派的前面delegate 改用 => 放在 () 與 {} 中間 所以一個空白的 lambda 如下 ```csharp //匿名委派 delegate() {} //lambda () => { } ``` 這一個寫法是源自於數學上用來表示函數的Lambda的匿名寫法 所以會這樣寫其實也是抄過來的 (咳...) 而Lambda 會由編譯器進行解析後幫我們產生相對應的code 或是 樹狀表達式 所以說開頭的範例我們就可以這樣寫 ```csharp public class SampleClass { public static void Main(string[] args) { ((Action)(() => Console.WriteLine("HelloWorld"))) .Invoke(); } } ``` 如果你覺得這樣寫一大堆的括號很醜很混亂, 這樣想是對的因為這是為了示範而表現的過程 正常熟悉lambda的人也不會這樣, 但後續會持續地重複的使用上述的概念來解釋後面的事情 所以 只要大概了解lambda 是怎麼從 method 變過來的即可 #### 備註 在 C# 中 存在兩個公定的委派 分別叫做 Action and Func Action 表示無回傳參數的委派,泛型型別為參數且與參數個數相同 Func 表示具有回傳參數的委派,泛型型別為參數且與參數個數相同且最後一個泛型參數為回傳型別 所以在使用lambda 時常常會與上述的委派型別搭配使用 ```csharp Action action = () => Console.WriteLine("Hello World"); Func<string> func = () => "Hello World"; ``` ### 參數化 如果你已經很熟悉將lambda 運用於參數建立具有委派參數的方法以及 Linq 的話可以跳過此章節 #### 是方法也是參數 一般來說使用方法時都會先給予方法一些參數已得到期望相對應的結果 而當方法可以被匿名宣告出來之後 仍然需要一個名稱去指向他 所以如果我們用本地變數來做的話就像是下面的範例 ```csharp Func<int,int,int> Add = (x,y) => x + y Add(1,2) // 3 Add(3,-1) // 2 ``` 而變數可以成為別的方法的參數 所以當一個方法成為另一個方法的參數時 其實也就是把執行這個方法的動作 讓另一個方法去執行 這其實也是很常見的事情 就是 Event 事件 只是現在的事件已經多到族繁不及備載 不過通常事件都不會用具有回傳值的方法(因為無法有效整合) #### 計算機範例 下面是一個計算機的例子 ```csharp public int Add(int x, int y) { return x + y; } public Func<int, int, int> Minus = (x, y) => x - y; public int Compute(int a, int b, Func<int, int, int> operator) { return operator(a, b); } Compute(1, 2, Add); // 3 Compute(1, 2, Minus); // -1 Compute(1, 2, (x, y) => x * y); // 2 ``` 這看似多餘的Compute方法是為了表示可以如何呈現以方法或委派來供提Compute如何進行運算 這樣的做法被運用在很多的地方 ex: Configuration, Transform(like map), Condition 而會這樣用往往原因是因為提供者無法提供足以滿足使用者所期望的項目 所以不如就讓使用者自己決定吧 以上述的案例來解釋就是 a, b 兩者間的運算太多可能性了,不如你告訴我你想怎麼算吧 ### 方法間的運算 此處已開始涉及moned的範圍, 對moned 有興趣的可以從此處開始 如果仍對於lambda 表示式不熟悉 建議可以把接下來的lambda 以前面解說的方式轉換成為自己熟悉情況 #### 以方法創造新的方法 讓我們用前一篇最後的計算機範例來稍作改變 ```csharp public T Compute<T>(T a, T b, Func<T, T, T> operator) { return operator(a, b); } Compute<Func<string, string>>( x => x + "a", x => x + "b", ?); ``` 此時參數 operator 到底應該怎麼辦呢? 讓我們用前面的做法 先把它變回常見的方法 ```csharp public Func<string,string> Operator( Func<string, string> x, Func<string, string> y ) { // 回傳方法最簡單的方式就是用lambda // 所以Func<string,string> 表示有一參數且要回傳值 return input => { // 這裡用最簡易的方式來運算x 與 y: // 先執行x 再將其結果用來執行y 再把y 的結果回傳 var xResult = x(input); var yResult = y(xResult); return yResult; }; } // 上述方法可以用 (first, last) => input => last(first(input)) 來取代 ``` (first, last) => input => last(first(input)); 表示有兩個參數first, last (型別未提及) 帶入後會獲得一個需要帶入一個參數為 input 的方法並且具有回傳值 該回傳的方法內容是將input先帶入first方法再帶入last方法後回傳結果 這裡再進一步的解說這兩個箭頭到底是什麼鬼東西 我們先把箭頭右邊的東西遮蔽掉用 Result來取代 (first, left) => Result 這表示當代入 first , left 兩個參數之後 會得到 Result 部分的結果 此時再把 Result 還原成原本的樣子 input => last(first(input)) 而這時後因為 first和 last 在前一次方法中放進這個結果中 所以 last 與 first 才得以被調用 #### 備註 這一張的內容其實還有很多借用的知識,像是閉包, 柯理化等等 ### Monad // 此處開始解釋什麼是Monad 未完成 #### ## 參考連結 (Lambda wiki)[https://zh.wikipedia.org/wiki/%CE%9B%E6%BC%94%E7%AE%97] (如何用c#解釋monad)[https://mikhail.io/2018/07/monads-explained-in-csharp-again/] (不用害怕monads/Brain Beckman)[https://channel9.msdn.com/Shows/Going+Deep/Brian-Beckman-Dont-fear-the-Monads#c633313640460000000]