2017-09-26 57 views
4

試圖瞭解evancz/URL解析器模塊,我偶然發現了這種類型的聲明,我很難理解:(source瞭解這個榆木網址解析器解析器類型聲明

type Parser a b = 
    Parser (State a -> List (State b)) 

事實上,「分析器」作爲類型名稱出現,並且在類型定義內部尤其令人不安。

有人能用英文解釋類型註釋嗎?例如「給定兩個抽象類型a和b,...?」

非常感謝。

回答

5

有幾件事情在這裏解壓,讓我們來分解它:

類型名稱可以具有相同名稱的構造函數。這是有效的代碼:以上

type Foo a = Foo a 

Foo類型需要一個單一類型的參數,有一個單一的方法來創建Foo a類型的值,通過使用它的一個構造函數恰好相同的名稱。這讓我們定義不同類型的FOOS,像這樣:

fooString : Foo String 
fooString = Foo "abc" 

fooInt : Foo Int 
fooInt = Foo 123 

在上述例子中,Foo充當一個字符串或int值的容器。但這並不完全可以解決。由於函數是Elm中的值,因此可以使用一個Foo來保存函數。讓我們定義一個函數,它接受一個整數,並增加了一個它:

plusOne : Int -> Int 
plusOne = (+) 1 

現在,讓我們換行了一個Foo值:

fooPlusOner : Foo (Int -> Int) 
fooPlusOner = Foo plusOne 

這完全是合法的代碼。類型Foo (Int -> Int)的值僅僅是一個函數的包裝。所以現在我們正在封裝一個函數,我們該如何處理它呢?讓我們創建一個函數,運行fooPlusOner功能,給一個整數作爲起點:

runFooIntFunc : Int -> Foo (Int -> Int) -> Int 
runFooIntFunc val (Foo f) = f val 

如果你像這樣運行runFooIntFunc 3 fooPlusOner此功能,您將收到價值4

我們可以概括這個功能有點擺脫明確使用int的:

runFooFunc : a -> Foo (a -> a) -> a 
runFooFunc val (Foo f) = f val 

現在,這將與返回相同類型作爲其輸入的任何功能工作。比方說,我們希望有一個富功能,增加了一個感嘆號爲任何字符串:

fooShouter : Foo (String -> String) 
fooShouter = Foo (\s -> s ++ "!") 

運行runFooFunc "wow" fooShouter將返回"wow!"

現在,讓我們打破正在發生的事情的分析器定義:

type Parser a b = 
    Parser (State a -> List (State b)) 

注意,Parser構造簡單包裝State a -> List (State b)類型的函數。不幸的是,State類型是不透明的(非導出),所以我們不能直接對它寫代碼,但是你可以定義你自己的狀態並玩弄它。

不要太落實實施細節,記住它只是某種類型功能的包裝。所以問題可能是,爲什麼要這樣寫呢?

嗯,這個實現使Parsers在解析器上以隱藏實現細節的方式更容易,爲原語解析器提供了良好的基礎,允許一種優雅的方式來爲分析器分層而不必擔心狀態。處理分析器和解碼器時,這種類型的模式常常在功能語言中出現,或者任何圍繞狀態的東西都可以看到。

閱讀Haskell內部的State monad介紹可能會有所幫助。類型是不同的,但許多底層概念是共享的。

3

「Parser」作爲類型名稱出現在類型定義中的事實特別令人不安。

是的,乍一看這是一個有點混亂,但什麼實際需要在這一行來理解:

type Parser a b = 
    Parser (State a -> List (State b)) 

同一個詞Parser用於方便。

讓我們用不同的名字了片刻:

type TypeParser a b = 
    DataParser (State a -> List (State b)) 

TypeParser類型構造。它接受兩個參數並返回一個類型。它的使用,例如,當你定義另一種類型:

type alias Model = { parser : TypeParser Int Int } 

DataParser數據構造。它是用於構建數據,這將是類型TypeParser a b

parser = DataParser (\state -> [state]) 

Elm允許使用相同名稱typedata構造。

有一個在文件鏈接詳盡的例子您提供的哪些:

map : a -> Parser a b -> Parser (b -> c) c 

這裏的Parser在類型標註使用。

而且也用於模式匹配的Parser a b類型的參數,並建立使用Parser數據的構造函數中定義一個Parser (b -> c) c類型的值:

map subValue (Parser parse) = 
    Parser <| \{ visited, unvisited, params, value } -> 
    List.map (mapHelp value) <| parse <| 
     { visited = visited 
     , unvisited = unvisited 
     , params = params 
     , value = subValue 
     }