理解这个elm url-parser Parser类型声明

时间:2017-09-26 13:40:12

标签: types elm

试图理解evancz / url-parser模块,我偶然发现了我难以理解的这种类型声明:(source

||

" Parser"作为类型名称出现并且在类型定义中特别令人不安。

有人可以用英语解释类型注释吗? 例如"给出两个抽象类型a和b,......?"

非常感谢。

2 个答案:

答案 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允许typedata构造函数使用相同的名称。

您提供的文件链接中有一个详尽的示例:

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
      }