跳轉到內容

Haskell/Arrows

來自華夏公益教科書,開放的書籍,為開放的世界

箭頭是單子的推廣:每個單子都會產生一個箭頭,但並非所有箭頭都會產生單子。它們與單子有著相同的目的——為庫提供通用結構——但更為通用。特別是,它們允許部分靜態(獨立於輸入)或可能接受多個輸入的計算概念。如果你的應用程式可以用單子很好地工作,你最好堅持使用它們。但如果你正在使用一個非常類似於單子但不是單子的結構,那麼它可能就是一個箭頭。

proc 和箭頭尾部

[編輯 | 編輯原始碼]

讓我們首先了解箭頭的表示法。我們將使用最簡單的箭頭(函式)並構建一些玩具程式,其目的僅僅是為了熟悉語法。

開啟你的文字編輯器並建立一個 Haskell 檔案,比如 toyArrows.hs

{-# LANGUAGE Arrows #-}

import Control.Arrow (returnA)

idA :: a -> a
idA = proc a -> returnA -< a

plusOne :: Int -> Int
plusOne = proc a -> returnA -< (a+1)

這些是我們前兩個箭頭。第一個是箭頭形式的恆等函式,第二個稍微更有趣,是一個將輸入加一的箭頭。在 GHCi 中載入它,使用 -XArrows 擴充套件並檢視會發生什麼。

% ghci -XArrows toyArrows.hs   
   ___         ___ _
  / _ \ /\  /\/ __(_)
 / /_\// /_/ / /  | |      GHC Interactive, version 6.4.1, for Haskell 98.
/ /_\\/ __  / /___| |      http://www.haskell.org/ghc/
\____/\/ /_/\____/|_|      Type :? for help.

Loading package base-1.0 ... linking ... done.
Compiling Main             ( toyArrows.hs, interpreted )
Ok, modules loaded: Main.
*Main> idA 3
3
*Main> idA "foo"
"foo"
*Main> plusOne 3
4
*Main> plusOne 100
101

的確令人激動。到目前為止,我們已經在箭頭表示法中看到了三個新的構造

  • 關鍵字 proc
  • -<
  • 匯入的函式 returnA

既然我們已經知道如何將一個值加一,讓我們嘗試一個更困難的兩倍的嘗試:加二

 plusOne = proc a -> returnA -< (a+1)
 plusTwo = proc a -> plusOne -< (a+1)

一個簡單的做法是將 (a+1) 作為輸入提供給 plusOne 箭頭。注意 plusOneplusTwo 之間的相似性。你應該注意到這裡有一個基本模式,它有點類似於:proc FOO -> SOME_ARROW <- (SOMETHING_WITH_FOO)

練習
  1. plusOne 是一個箭頭,因此根據上面的模式,returnA 也必須是一個箭頭。你認為 returnA 做了什麼?

do 標記

[編輯 | 編輯原始碼]

我們當前的 plusTwo 實現實際上相當令人失望……它不應該只是 plusOne 兩次嗎?我們可以做得更好,但要做到這一點,我們需要引入 do 標記

 plusTwoBis = 
  proc a -> do b <- plusOne -< a
               plusOne -< b

現在在 GHCi 中試試這個

Prelude> :r
Compiling Main             ( toyArrows.hs, interpreted )
Ok, modules loaded: Main.
*Main> plusTwoBis 5
7

你可以使用這個 do 標記構建任意長的序列

plusFive =
 proc a -> do b <- plusOne -< a
              c <- plusOne -< b
              d <- plusOne -< c
              e <- plusOne -< d
              plusOne -< e

單子與箭頭

[編輯 | 編輯原始碼]
FIXME:我不確定,但我相信這裡的意圖是展示使用這個 proc 標記與僅僅使用一個常規的 do 鏈的區別


華夏公益教科書