Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Site updated at 2012-04-19 13:25:45 UTC

  • Loading branch information...
commit 644d7eee35a9ac2572a02b3d4e210bcf7ed8a004 1 parent e94cc4b
@MnO2 authored
View
5 functors-applicative-functors-and-monoids.html
@@ -26,7 +26,10 @@
<li class="center"><a href="chapters.html">Index</a></li>
<li class="right"> <a href="a-fistful-of-monads.html">來看看幾種Moand</a><img src="img/nxt.png"></img></li>
</ul>
- <a name="Functors, Applicative Functors與Monoids"></a><h1>Functors, Applicative Functors與Monoids</h1><a name="溫習Functors"></a><h2>溫習Functors</h2><a name="Applicative functors"></a><h2>Applicative functors</h2><a name="關鍵字"newtype""></a><h2>關鍵字"newtype"</h2><a name="Monoids"></a><h2>Monoids</h2>
+ <a name="Functors, Applicative Functors與Monoids"></a><h1>Functors, Applicative Functors與Monoids</h1><p>Haskell的一些特色,像是純粹性,高階函數,algebraic data types,typeclasses,</p><p>這些讓我們可以從更高的角度來看到polymorphism這件事。</p><p>不像OOP當中需要從龐大的型態階層來思考。</p><p>我們只需要看看手邊的型態的行為,將他們跟適當地typeclass對應起來就可以了。</p><p>像<code>Int</code>的行為跟很多東西很像。</p><p>好比說他可以比較相不相等,可以從大到小排列,也可以將他們一一窮舉出來。</p><p>Typeclass的運用是很隨意的。</p><p>我們可以定義自己的資料型態,然後描述他可以怎樣被操作,跟typeclass關聯起來便定義了他的行為。</p><p>由於Haskell強大的型態系統,</p><p>這讓我們只要讀函數的型態宣告就可以知道很多資訊。</p><p>typeclass可以定義得很抽象很general。</p><p>我們之前有看過typeclass定義了可以比較兩個東西是否相等,或是定義了可以比較兩個東西的大小。</p><p>這些是既抽象但又描述簡潔的行為,</p><p>但我們不會認為他們有什麼特別之處,因為我們時常碰到他們。</p><p>最近我們看過了functor,基本上他們是一群可以被map over的物件。</p><p>這是其中一個例子能夠抽象但又漂亮地描述行為。</p><p>在這一章中,</p><p>我們會詳加闡述functors,</p><p>並會提到比較強一些的版本,也就是applicative functors。</p><p>我們也會提到monoids。</p><a name="溫習Functors"></a><h2>溫習Functors</h2><p>我們已經在之前的章節提到functors。</p><p>如果你還沒讀那個章節,</p><p>也許你應該先去看看。</p><p>或是你直接假裝你已經讀過了。</p><p>來快速複習一下:</p><p>Functors是可以被map over的物件,</p><p>像是lists,<code>Maybe</code>,trees等等。</p><p>在Haskell中我們是用<code>Functor</code>這個typeclass來描述他。</p><p>這個typeclass只有一個method,叫做<code>fmap</code>,</p><p>他的型態是<code>fmap :: (a -> b) -> fa -> f b</code>。</p><p>這型態說明了如果給我一個從<code>a</code>映到<code>b</code>的函數,以及一個裝了<code>a</code>的盒子,</p><p>我會回給你一個裝了<code>b</code>的盒子。</p><p>就好像用這個函數將每個元素都轉成<code>b</code>一樣</p><blockquote><p><b>給一點建議</b>。這盒子的比喻嘗試讓你抓到些functors是如何運作的感覺。在之後我們也會用相同的比喻來比喻applicative functors跟monads。在多數情況下這種比喻是恰當的,但不要過度引申,有些functors是不適用這個比喻的。一個比較正確的形容是functors是一個計算語境(computational context)。這個語境可能是這個computation可能帶有值,或是有可能會失敗(像<code>Maybe</code>跟<code>Either a</code>),或是他可能有多個值(像lists),等等。</p></blockquote><p>如果一個type constructor要是<code>Functor</code>的instance,那他的kind必須是<code>* -> *</code>,</p><p>這代表他必須剛好接受一個type當作type parameter。</p><p>像是<code>Maybe</code>可以是Functor的一個instance,</p><p>因為他接受一個type parameter,來做成像是<code>Maybe Int</code>,或是<code>Maybe String</code>。</p><p>如果一個type constructor接受兩個參數,</p><p>像是<code>Either</code>,我們必須給他兩個type parameter。</p><p>所以我們不能這樣寫:<code>instance Functor Either where</code>,</p><p>但我們可以寫<code>instance Functor (Either a) where</code>,</p><p>如果我們把<code>fmap</code>限縮成只是<code>Either a</code>的,</p><p>那他的型態就是<code>fmap :: (b -> c) -> Either a b -> Either a c</code>。</p><p>就像你看到的,<code>Either a</code>的是固定的一部分,</p><p>因為<code>Either a</code>只恰好接受一個type parameter,</p><p>但<code>Either</code>則要接球兩個type parameters。</p><p>這樣fmap的型態變成<code>fmap :: (b -> c) -> Either b -> Either c</code>,這不太合理。</p><p>我們知道有許多型態都是<code>Functor</code>的instance,</p><p>像是<code>[]</code>,<code>Maybe</code>,<code>Either a</code>以及我們自己寫的<code>Tree</code>。</p><p>我們也看到了如何用一個函數map他們。</p><p>在這一章節,我們再多舉兩個例子,也就是<code>IO</code>跟<code>(->) r</code>。</p><p>如果一個值的型態是<code>IO String</code>,</p><p>他代表的是一個會被計算成String結果的I/O action。</p><p>我們可以用do syntax來把結果綁定到某個名稱。</p><p>我們之前把I/O action比喻做長了腳的盒子,會到真實世界幫我們取一些值回來。</p><p>我們可以檢視他們取了什麼值,</p><p>但一旦看過,我們必須要把值放回盒子中。</p><p>用這個比喻,<code>IO</code>的行為就像是一個functor。</p><p>我們來看看<code>IO</code>是怎麼樣的一個<code>Functor</code>instance。</p><p>當我們<code>fmap</code>用一個function來map over I/O action時,</p><p>我們會想要拿回一個裝著已經用function映射過值的I/O action。</p><pre class="code">instance Functor IO where
+ fmap f action = do
+ result <- action
+ return (f result)</pre><a name="Applicative functors"></a><h2>Applicative functors</h2><a name="關鍵字"newtype""></a><h2>關鍵字"newtype"</h2><a name="Monoids"></a><h2>Monoids</h2>
<ul class="nav">
<li class="left"> <img src="img/prv.png"></img><a href="functionally-solving-problems.html">函數式地思考來解決問題</a></li>
<li class="center"><a href="chapters.html">Index</a></li>
View
2  index.html
@@ -29,7 +29,7 @@
<p>這份翻譯是奠基於<a href="http://fleurer-lee.com">Fleurer</a>在2008年所翻譯的<a href="http://fleurer-lee.com/lyah/">前八章</a>,將其轉成繁體中文。並繼續翻譯第九章到第十四章,直到完成,前八章的部分也會陸續修改成繁體的一些使用習慣。可以跟<a href="http://learnyouahaskell.com">原作英文版</a>搭配閱讀。</p>
<p>繁體譯者為<a href="http://blog-mno2.csie.org">MnO2</a>,電子郵件是<img src="img/mymail.png" />,或者你可以在<a href="https://plus.google.com/103925382602424793947">google plus</a>找到我。如果你對於整份教學想要更正,或者想要複製一份到自己的網站,可以從<a href="https://github.com/learnyouahaskell-zh-tw/learnyouahaskell-zh-tw.github.com/tree/source">這邊</a>用git clone一份,歡迎送patch給我或直接寫E-mail跟我說明哪些地方可以改進。工具程式的部份是直接改寫<a href="http://code.google.com/p/fdoc/">Fleurer所寫的小程式</a>,採用<a href="http://dev.perl.org/licenses/">GPL/Artistic雙授權</a></p>
<p>如果你對Haskell十分有興趣,可以到<a href="http://zh-haskell.csie.org/">這邊</a>留下你的E-mail。大家有機會認識認識</p>
- <p>最後更新時間: 2012/04/16</p>
+ <p>最後更新時間: 2012/04/19</p>
</div>
<div id="go">
<p><a id="read" href="chapters.html">Read it Online!</a></p>
View
2  input-and-output.html
@@ -32,7 +32,7 @@
hello, world</pre><p>這就是我們第一個編譯成功並印出字串到螢幕的程式。很簡單吧。</p><p>讓我們來看一下我們究竟做了些什麼,</p><p>首先來看一下<code>putStrLn</code>函數的型態</p><pre class="code">ghci> :t putStrLn
putStrLn :: String -> IO ()
ghci> :t putStrLn "hello, world"
-putStrLn "hello, world" :: IO ()</pre><p>我們可以這麼解讀<code>putStrLn</code>的型態:</p><p><code>putStrLn</code>接受一個字串並回傳一個I/O action,這I/O action包含了<code>()</code>的型態。</p><p>(即空的tuple,或者是unit型態)。</p><p>一個I/O action是一個會造成副作用的動作,常是指讀取輸入或輸出到螢幕,</p><p>同時也代表會回傳某些值。</p><p>在螢幕印出幾個字串並沒有什麼有意義的回傳值可言,</p><p>所以這邊用一個<code>()</code>來代表。</p><p>那究竟I/O action會在什麼時候被觸發呢?</p><p>這就是<code>main</code>的公用所在。</p><p>一個I/O action會在我們把它綁定到<code>main</code>這個名字並且執行程式的時候觸發。</p><p>把整個程式限制在只能有一個I/O action看似是個極大的限制。</p><p>這就是為什麼我們需要do表示法來將所有I/O action綁成一個。</p><p>來看看下面這個例子。</p><pre class="code">main = do
+putStrLn "hello, world" :: IO ()</pre><p>我們可以這麼解讀<code>putStrLn</code>的型態:</p><p><code>putStrLn</code>接受一個字串並回傳一個I/O action,這I/O action包含了<code>()</code>的型態。</p><p>(即空的tuple,或者是unit型態)。</p><p>一個I/O action是一個會造成副作用的動作,常是指讀取輸入或輸出到螢幕,</p><p>同時也代表會回傳某些值。</p><p>在螢幕印出幾個字串並沒有什麼有意義的回傳值可言,</p><p>所以這邊用一個<code>()</code>來代表。</p><p>那究竟I/O action會在什麼時候被觸發呢?</p><p>這就是<code>main</code>的功用所在。</p><p>一個I/O action會在我們把它綁定到<code>main</code>這個名字並且執行程式的時候觸發。</p><p>把整個程式限制在只能有一個I/O action看似是個極大的限制。</p><p>這就是為什麼我們需要do表示法來將所有I/O action綁成一個。</p><p>來看看下面這個例子。</p><pre class="code">main = do
putStrLn "Hello, what's your name?"
name <- getLine
putStrLn ("Hey " ++ name ++ ", you rock!")</pre><p>新的語法,有趣吧! 它看起來就像一個命令式的程式。</p><p>如果你編譯並執行它,它便會照你預期的方式執行。</p><p>我們寫了一個do並且接著一連串指令,就像寫個命令式程式一般,每一步都是一個I/O action。</p><p>將所有I/O action用do綁在一起變成了一個大的I/O action。</p><p>這個大的I/O action的型態是<code>IO ()</code>,</p><p>這完全是由最後一個I/O action所決定的。</p><p>這就是為什麼<code>main</code>的型態永遠都是<code>main :: IO something</code>,</p><p>其中<code>something</code>是某個具體的型態。</p><p>按照慣例,我們通常是不會把<code>main</code>的型態在程式中寫出來。</p><p>另一個有趣的事情是第三行<code>name <- getLine</code>。</p><p>它看起來像是從輸入讀取一行並存到一個變數<code>name</code>之中。</p><p>真的是這樣嗎? 我們來看看<code>getLine</code>的型態吧</p><pre class="code">ghci> :t getLine
Please sign in to comment.
Something went wrong with that request. Please try again.