Haskell lesson:類型系統解讀


最近忙成狗了,吭哧吭哧終於把家搬完了,以后就長久的住在新家了,由於剛搬家,網線還沒有接,又過了一個星期家里沒網的日子。這一段時間又沒時間好好整理haskell。甚是遺憾。從上次haskell實現Graham掃描算法到現在已經三周有余,這次分析一下haskell類型系統。

首先回憶一下C語言是怎么構造新類型的。

struct Test {
int index;
#define NAME_LEN 32
char name[NAME_LEN];
void (*get_name)(struct Test *);
};

這就是典型的C語言構造新類型的方法,即所謂的結構體。諸如此類的還有枚舉類型、聯合體。結構體定義了一個類型名字,成員都有其名字,並且根據名字訪問到各個成員。

一、代數數據類型(algebraic data type)與類型構造子(data constructor)

定義類型: data TypeName = TypeConstructor [param] [| TypeConstructor [param]]

其中param可有可無,但是要注意如果param存在,那么其肯定是一種類型。比如data Test = Test Int,表示傳給一個Int類型的參數給類型構造子Test,這時候在ghci中執行:t Test可以看到

*Main> :t Test
Test :: Int -> Test

還要注意第一個Test和第二個Test沒有任何聯系,第一個Test只是表示該類型名字叫Test,在代碼中實際使用的是第二個Test(也就是類型構造子)。

二、類型解構與模式匹配

在前面實現Graham掃描算法的例子中,我們使用了很多的函數,基本上都是通過模式匹配實現的。再舉個例子,譬如求和函數:

sum (x:xs) = x + sum xs
sum [] = 0

在第一次看到行如(x:xs)的時候,可能會有疑問–為什么有個括號?其實(x:xs)中的冒號(:)是一個類型構造子。在ghci中輸入:t (:)可以看到

*Main> :t (:)
(:) :: a -> [a] -> [a]

(:)就是用來構造列表的,而形如[1,2,3]的構造方式其實等價於(1:(2:(3:[])))。

說到這里,其實基本上就清楚了,模式匹配其實就是在對類型進行解構,使用的方法就是通過類型構造子的規定按照順序進行參數匹配。

現在對前面定義的Test類型進行解構,在ghci中輸入如下:

*Main> data Test=Test Int
*Main> getTest (Test a)=a
*Main> getTest (Test 10)
10

三、類型匹配

通過模式匹配得到類型之后,haskell還會進一步檢查類型是否匹配。

關於類型匹配,這里可以先簡單實驗一下,在ghci中輸入如下:

*Main> (\x->x+1)1
2
*Main>
*Main>
*Main> (\x->x+1)[]

<interactive>:37:1: error:
• Non type-variable argument in the constraint: Num [a]
(Use FlexibleContexts to permit this)
• When checking the inferred type
it :: forall a. Num [a] => [a]
*Main>

第一次輸入一個lambda表達式緊跟一個數1,這時候ghci給出了我們想要的結果2。當第二次我們入[]時,報的錯誤有點意思。

類型錯誤,這里有兩點信息:

  • lambda表達式是匿名函數,所以函數參數的匹配似乎和函數名字沒有直接關系。
  • 錯誤的直接原因顯示為類型不匹配,其實haskell自己已經通過類型系統推導出了當前輸入x的類型是Num [a], 而實際上運算+期待的參數是Num a,所以產生類型錯誤。

類型匹配就是由haskell的類型推導系統完成的。

四、newtype/type/data

前面都是關於自定義類型data type的分析,除了data關鍵字以外,haskell還提供newtype和type關鍵字。type只是給已有的類型取一個別名,類型上並不會改變,編譯器認為兩者其實是一個類型。而newtype則不同,他會新定義一個類型,但是又有所限制。newtype只能有一個值構造器,而且構造器恰好只有一個字段,只存在於編譯階段。

五、代數數據類型實例

// algebraic data type without params
data Node = Node Int Char deriving (Show)

// algebraic data type with params
data Node a = Node a deriving (Show)

// record syntax, funcname :: data type
data Customer = Customer {
customerID :: CustomerID
, customerName :: String
, customerAddress :: Address
}
deriving (Show)


// recursion data type definition
data Tree = Node Int Char (Tree) (Tree)
| Empty deriving (Show)

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



Haskell 筆記(三)類型系統 八、通用類型系統 swift的類型系統 類型系統的運算 什么是強類型系統? 類型系統的作用 golang中的類型系統 [golang note] 類型系統 javascript類型系統之Array erlang的類型系統一
 
粤ICP备14056181号  © 2014-2021 ITdaan.com