试图理解evancz / url-parser模块,我偶然发现了我难以理解的这种类型声明:(source)
||
" Parser"作为类型名称出现并且在类型定义中特别令人不安。
有人可以用英语解释类型注释吗? 例如"给出两个抽象类型a和b,......?"
非常感谢。
答案 0 :(得分:7)
这里有一些事要解开,所以让我们分解一下:
类型名称可以具有相同名称的构造函数。这是有效的代码:
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
。
我们可以稍微概括一下这个函数,以明确地使用Ints:
runFooFunc : a -> Foo (a -> a) -> a
runFooFunc val (Foo f) = f val
现在这适用于任何返回与其输入相同类型的函数。假设我们想要一个为任何字符串添加感叹号的Foo函数:
fooShouter : Foo (String -> String)
fooShouter = Foo (\s -> s ++ "!")
正在运行runFooFunc "wow" fooShouter
会返回"wow!"
。
现在,让我们分解Parser定义中发生的事情:
type Parser a b =
Parser (State a -> List (State b))
请注意,Parser
构造函数只是包装了State a -> List (State b)
类型的函数。不幸的是,State
类型是不透明的(非导出的),因此我们无法直接编写代码,但您可以定义自己的状态并使用它。
不要过多地执行细节,请记住它只是某种类型函数的包装器。所以问题可能是,为什么这样写呢?
嗯,实现使分析Parsers的方式更容易隐藏实现细节,提供原始解析器的良好基础,允许优雅的方式分层解析器而不必担心状态。在处理解析器和解码器时,或者在围绕状态的任何事情中,这种类型的模式经常出现在函数式语言中。
阅读Haskell内部State monad的介绍可能会有所帮助。类型不同,但许多基本概念是共享的。
答案 1 :(得分: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
允许type
和data
构造函数使用相同的名称。
您提供的文件链接中有一个详尽的示例:
map : a -> Parser a b -> Parser (b -> c) c
此处' Parser
用于类型注释。
还用于对Parser a b
类型的参数进行模式匹配,并在函数定义中使用Parser (b -> c) c
数据构造函数构建类型Parser
的值:
map subValue (Parser parse) =
Parser <| \{ visited, unvisited, params, value } ->
List.map (mapHelp value) <| parse <|
{ visited = visited
, unvisited = unvisited
, params = params
, value = subValue
}