試圖瞭解evancz/URL解析器模塊,我偶然發現了這種類型的聲明,我很難理解:(source)瞭解這個榆木網址解析器解析器類型聲明
type Parser a b =
Parser (State a -> List (State b))
事實上,「分析器」作爲類型名稱出現,並且在類型定義內部尤其令人不安。
有人能用英文解釋類型註釋嗎?例如「給定兩個抽象類型a和b,...?」
非常感謝。
試圖瞭解evancz/URL解析器模塊,我偶然發現了這種類型的聲明,我很難理解:(source)瞭解這個榆木網址解析器解析器類型聲明
type Parser a b =
Parser (State a -> List (State b))
事實上,「分析器」作爲類型名稱出現,並且在類型定義內部尤其令人不安。
有人能用英文解釋類型註釋嗎?例如「給定兩個抽象類型a和b,...?」
非常感謝。
有幾件事情在這裏解壓,讓我們來分解它:
類型名稱可以具有相同名稱的構造函數。這是有效的代碼:以上
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介紹可能會有所幫助。類型是不同的,但許多底層概念是共享的。
「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
允許使用相同名稱type
和data
構造。
有一個在文件鏈接詳盡的例子您提供的哪些:
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
}