2014-04-03 387 views
6

我以爲我對哈斯克爾單子辦理好,直到我意識到這一點很簡單的代碼沒有任何意義,我(這是從haskell wiki about the State monad):爲什麼我可以在不提供monad的情況下調用monadic函數?

playGame :: String -> State GameState GameValue 
playGame []  = do 
    (_, score) <- get 
    return score 

什麼讓我困惑的是,爲什麼是代碼當提供的唯一參數是一個字符串時允許調用「get」?看起來,它似乎幾乎是憑空取得價值。

對我來說,提出這個問題的一個更好的方法可能是,如何使用>>=和lambda來重寫這個函數而不是符號?我無法弄清楚自己。

+5

代碼不是「調用」得到的。 「get」不是一個函數,它是一個多態值:http://hackage.haskell.org/packages/archive/transformers/latest/doc/html/Control-Monad-Trans-State-Lazy.html#v:得到 –

+1

謝謝,這實際上是我的一個主要困惑點。對於來自面向對象領域的人來說,這幾乎是一個不幸的名字,其中類通常具有「getThis」和「getThat」方法。我只是假設由於「get」是一個動詞,它必須是一個函數。 –

+0

即使在OO土地上也不是那麼簡單。特別是因爲OO獲取器不是函數 - 它們實際上更接近於多態值,就像Haskell的get一樣。不同之處在於OO語言中的monad將該值附加到對象上,而我們將該值附加到具有任意語義的任意monad上。我會做出解釋,解釋我的意思。 – nomen

回答

8

脫糖到這個do符號看起來像

playGame [] = 
    get >>= \ (_, score) -> 
    return score 

我們也可以只寫這與fmap

playGame [] = fmap (\(_, score) -> score) get 
playGame [] = fmap snd get 

現在的關鍵是要認識到,get是像任何其他與價值類型

State s s 

get將返回將不會被確定,直到我們提供我們的計算runState或類似的地方,我們爲我們的國家提供明確的初始值。

如果我們簡化這一更進一步,擺脫了狀態單子我們不得不

playGame :: String -> (GameState -> (GameState, GameValue)) 
playGame [] = \gamestate -> (gamestate, snd gamestate) 

狀態單子只是披着所有的GameState本手冊的傳球,但你能想到的get作爲訪問我們的「功能」通過的價值。

+0

因此,從某種意義上說,國家monad正在隱藏國家的消逝(正如你所提到的)。我可以欣賞這裏的「魔術」。但是,runState的膽量當然是「獲得」的價值「持有」的地方......對吧? –

+1

@ parker.sikand因此'State'只是一個字面函數's - >(s,a)'上的包裝器。 'runState'就是我們用來實際應用這個函數的東西。你可以把它看作是一種「加速」的''''。所以我認爲'runState'和'(\ x - > ...)一樣保存'get'的值$ a''$'保存'x'的值 – jozefg

0

monad是一個「事物」,它需要一個上下文(我們稱之爲m),以及哪個「產生」了一個值,同時仍尊重monad法則。我們可以從單子的「內部」和「外部」的角度來考慮這一點。單子法告訴我們如何處理「往返」 - 去外面然後再回來。特別是,法律告訴我們m(m a)與(m a)基本上是相同的類型。

重點是monad是這種往返事物的概括。 (m(m a))'加入(m a)'中,並且(>> =)從monad中拉出一個值並將一個函數應用到monad中。換句話說,它將函數(f :: a - > m b)應用於a(m a)中 - 這將產生一個(m(m b)),然後通過連接壓縮以獲得我們的(m b)。

那麼這與'get'和對象有什麼關係呢?

那麼,記號使我們起來,以便計算的結果在我們的monad中。並且(< - )讓我們從monad中取出一個值,以便我們可以將它綁定到一個函數,同時名義上還在monad中。因此,例如:

doStuff = do 
    a <- get 
    b <- get 
    return $ (a + b) 

請注意a和b是純的。他們是「外」的,因爲我們真的在裏面偷看。但是現在我們已經在monad之外獲得了一個價值,我們需要對它做一些事情(+),然後把它放回monad中。

這是啓發性的符號的只是一點點,但如果我們能做到這一點可能是好的:

doStuff = do 
    a  <- get 
    b  <- get 
    (a + b) -> (\x -> return x) 

真正強調的背部和中闡述它。當你完成一個monad動作時,你必須在位於該表的右列,因爲當動作完成時,將會調用'join'來平鋪圖層。 (至少在概念上)

哦,對,對象。顯然,OO語言基本上是以某種IO單元生活和呼吸的。但我們實際上可以再分解一些。當您運行的線沿線的東西:

x = foo.bar.baz.bin() 

你基本上是運行一個單子轉換棧,這需要一個IO背景下,其產生的foo的背景下,其產生的酒吧背景下,其產生的巴茲背景下,這產生一個bin上下文。然後運行時系統根據需要多次「加入」這個東西。請注意,這個想法與「調用堆棧」有何關係。事實上,這就是爲什麼它在haskell方面被稱爲「monad變壓器堆棧」。它是一堆monadic上下文。

相關問題