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
是一個類型構造器,提供兩個類型Int
和String
,得到類型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
Nothing
和Just
是Maybe 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)