[Haskell] State Monad

1. 類型

類型用來區分內存中對程序員來說不同種類的數據塊
而內存中存儲的是表達式的值
所以,區分內存塊的類型,就是區分表達式的類型

靜態類型,指的是在編譯時就能確定表達式的類型
動態類型,指的是在運行時才能確定表達式的類型

注:
靜態類型,也可以說,通過代碼文本就能確定表達式的類型

強類型和弱類型是相對而言的
類型系統的強度指的是,在多大程度上能把表達式看做是合法類型的
類型強度的表現形式是,是否允許隱式類型轉換
弱類型,更允許隱式類型轉換
強類型,更不允許隱式類型轉換

2. 定義類型

Haskell中定義類型的方式有以下幾種

(1)使用type定義類型別名

type MyTuple = (Int, String)

定義MyTuple為元組類型(Int, String)的別名

(2)使用data定義新類型

data BookType = BookValue Int String

其中,BookType是類型名,又稱為類型構造器,BookValue稱為值構造器

一個BookType類型值,可以這樣定義

let bookValue = BookValue 12 "ab"

注:
因為類型構造器和值構造器會在不同上下文中使用,所以經常把它們寫為同一個名字

data Book = Book Int String

遇到名字Book時要小心區分

(3)使用newtype為已有類型定義新的標識
newtype定義的類型,只能有一個值構造器,且值構造器只能有一個字段

例如:
一個值構造器,值構造器只有一個字段,可以

newtype Okay = ExactlyOne Int

類型構造器有多個參數,但是只有一個值構造器,值構造器只有一個字段,也可以

newtype Param a b = Param (Either a b)

字段訪問器語法,可以

newtype Record = Record {
    getInt :: Int
}

注:
字段訪問器語法,同時定義了字段的類型Int,以及字段的訪問器函數getInt :: Record -> Int
例如:
通過值構造器Record構造該類型的值,Record 12
通過訪問器提取字段的值,getInt (Record 12)

有一個值構造器,但是值構造器沒有字段,不可以

newtype TooFew = TooFew

有一個值構造器,但是值構造器有兩個字段,不可以

newtype TooManyFields = Fields Int Int

有多個值構造器,不可以

newtype TooManyCtors = Bad Int
    | Worse Int

3. 帶參數的類型

類型構造器也可以帶參數

data Maybe a = Nothing
   | Just a

data Either a b = Left a
    | Right b

其中,Maybe是類型構造器,Maybe Int才是一個具體的類型
Either是類型構造器,Either Int String才是一個具體的類型

注:
與函數的Currying相似,類型構造器也可以Currying
Either是一個類型構造器,提供兩個類型IntString,得到類型Either Int String
Either Int也是一個類型構造器,提供類型String,得到類型Either Int String

4. newtype

使用newtype為已有類型定義編譯時的新標識,
這個標識在運行時會轉換為原有的類型,可以稱為去殼

例如:
定義新的類型

data DataInt = D Int
    deriving (Eq, Ord, Show)

定義編譯時的類型外殼

newtype NewtypeInt = N Int
    deriving (Eq, Ord, Show)

運行時,求值undefined會發生錯誤

ghci> undefined
*** Exception: Prelude.undefined

運行時,_可以匹配所有,undefined不求值

ghci> case D undefined of D _ -> 1
1

運行時,N外殼會去掉,即case undefined of _ -> 1_可以匹配所有,undefined不求值

ghci> case N undefined of N _ -> 1
1

運行時,D _來匹配undefined的求值結果,undefined要求值,發生錯誤

ghci> case undefined of D _ -> 1
*** Exception: Prelude.undefined

運行時,N外殼會去掉,即case undefined of _ -> 1_可以匹配所有,undefined不求值

ghci> case undefined of N _ -> 1
1

5. typeclass

類型類提取了相似的運算
類型類的實例是一個類型構造器,這個類型構造器構造的類型具有類型類指定的運算

注:
類型類的實例,是一個類型構造器
類型構造器,可以是零元類型構造器(具體的類型),也可以帶參數

例如:

class BasicEq a where
    isEqual :: a -> a -> Bool

其中,BasicEq類型類,定義了isEqual函數

指定類型構造器Bool(零元類型構造器,即具體的類型)是BasicEq類型類的實例
于是,isEqual就具體化為isEqual :: Bool -> Bool -> Bool

instance BasicEq Bool where
    isEqual True  True  = True
    isEqual False False = True
    isEqual _     _     = False

6. Monad

Monad是一個類型類
針對一般化的鏈式操作,它定義了>>=return兩種運算

class Monad m where
    -- chain
    (>>=)  :: m a -> (a -> m b) -> m b
    -- inject
    return :: a -> m a

其中,Monad類型類的實例,是一個單參類型構造器

注:
m a類型的值,通常稱之為action
有時候,返回action的函數,也稱之為action

instance Monad Maybe where
    Nothing >>= f = Nothing
    Just a >>= f = f a
    return = Just

其中,>>=具體化為了(>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
return具體化為了return :: a -> Maybe a
f的類型是f :: a -> Maybe b
NothingJustMaybe a類型的值構造器

data Maybe a = Nothing
    | Just a

注:
Monad還定義了兩種操作>>fail

(>>) :: m a -> m b -> m b
a >> f = a >>= \_ -> f

>>并不關心a的執行結果,只是進行順序調用

fail :: String -> m a
fail = error

7. do語法塊

do語法塊會在編譯時轉換為>>=>>return形式

(1)單個action

doNotation1 =
    do act

translated1 =
    act

(2)多個action

doNotation2 =
    do act1
       act2
       {- ... etc. -}
       actN

translated2 =
    act1 >>
    do act2
       {- ... etc. -}
       actN

finalTranslation2 =
    act1 >>
    act2 >>
    {- ... etc. -}
    actN

(3)帶有<-的action

doNotation3 =
    do pattern <- act1
       act2
       {- ... etc. -}
       actN

translated3 =
    let f pattern = do act2
                       {- ... etc. -}
                       actN
        f _ = fail "..."
    in act1 >>= f

(4)let

doNotation4 =
    do let val1 = expr1
           val2 = expr2
           {- ... etc. -}
           valN = exprN
       act1
       act2
       {- ... etc. -}
       actN

translated4 =
    let val1 = expr1
        val2 = expr2
        valN = exprN
    in do act1
          act2
          {- ... etc. -}
          actN

8. State Monad

為原有類型定義一個新的標識

newtype State s a = State {
        runState :: s -> (a, s)
    }

其中,類型構造器是State
字段的類型是s->(a,s)是一個函數
字段訪問器是runState

注:
因為State是一個雙參類型構造器,
所以State s是一個單參類型構造器
runState的類型是runState :: State s a -> s -> (a, s)

指定State s是Monad類型類的實例

instance Monad (State s) where
...

注:
Monad類型類的實例,是一個單參類型構造器,
所以,State s是Monad類型類的實例,State不是

returnState :: a -> State s a
returnState a = State $ \s -> (a, s)

bindState :: State s a -> (a -> State s b) -> State s b
bindState m k = State $ \s -> let (a, s') = runState m s
                                                    in runState (k a) s'

其中,m的類型是m :: State s a
k的類型是k :: a -> State s b
runState的類型是runState :: State s a -> s -> (a, s)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 初識狀態 先看一個小游戲,這個小游戲用字符串來控制,最后得到總分。c用來啟動和停止計分,在啟動計分狀態下a加一分,...
    何幻閱讀 1,047評論 0 0
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,981評論 19 139
  • 本文為翻譯,個人學習之用,原地址 程序狀態 如果你以前有其他語言的編程經驗,你可能寫過一些函數或者方法來控制程序的...
    ParkinWu閱讀 3,082評論 0 3
  • 前言 近期又開始折騰起Haskell,掉進這個深坑恐怕很難再爬上來了。在不斷深入了解Haskell的各種概念以及使...
    Tangentw閱讀 2,164評論 0 9
  • 驕陽如火 殘暴的炙烤著大地 悶熱的天 壓抑的讓人透不過氣來 獨坐窗前 思緒萬千 一路走來 肩上的行囊空空如...
    不在彼岸只在海閱讀 227評論 2 2