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

Ale*_*xHv 7 types elm

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

type Parser a b =
  Parser (State a -> List (State b))
Run Code Online (Sandbox Code Playgroud)

"Parser"作为类型名称出现在类型定义中的事实尤其令人不安.

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

非常感谢.

Cha*_*ert 7

这里有一些东西需要解压缩,所以让我们分解一下:

类型名称可以具有相同名称的构造函数.这是有效的代码:

type Foo a = Foo a
Run Code Online (Sandbox Code Playgroud)

上面的类型Foo采用单个类型参数,并且Foo a通过使用恰好共享相同名称的单个构造函数,只有一种方法可以创建类型的值.这让我们可以定义不同类型的Foos,如下所示:

fooString : Foo String
fooString = Foo "abc"

fooInt : Foo Int
fooInt = Foo 123
Run Code Online (Sandbox Code Playgroud)

在上面的示例中,Foo充当字符串或int值的容器.但这不是它可以坚持的全部.由于函数是Elm中的值,因此您可以Foo拥有一个函数.让我们定义一个带整数并向其中加一的函数:

plusOne : Int -> Int
plusOne = (+) 1
Run Code Online (Sandbox Code Playgroud)

现在,让我们将其包装成一个Foo值:

fooPlusOner : Foo (Int -> Int)
fooPlusOner = Foo plusOne
Run Code Online (Sandbox Code Playgroud)

这是完全有效的代码.类型的Foo (Int -> Int)值只是函数的包装器.那么现在我们正在包装一个函数,我们怎么能用它做点什么呢?让我们创建一个在里面运行函数的函数fooPlusOner,给出一个整数作为起点:

runFooIntFunc : Int -> Foo (Int -> Int) -> Int
runFooIntFunc val (Foo f) = f val
Run Code Online (Sandbox Code Playgroud)

如果您像这样运行此函数runFooIntFunc 3 fooPlusOner,则会收到该值4.

我们可以稍微概括一下这个函数,以便明确地使用Ints:

runFooFunc : a -> Foo (a -> a) -> a
runFooFunc val (Foo f) = f val
Run Code Online (Sandbox Code Playgroud)

现在,这适用于任何返回与其输入相同类型的函数.假设我们想要一个为任何字符串添加感叹号的Foo函数:

fooShouter : Foo (String -> String)
fooShouter = Foo (\s -> s ++ "!")
Run Code Online (Sandbox Code Playgroud)

跑步runFooFunc "wow" fooShouter会回来"wow!".

现在,让我们分解Parser定义中发生的事情:

type Parser a b =
    Parser (State a -> List (State b))
Run Code Online (Sandbox Code Playgroud)

请注意,Parser构造函数只是包装了一个类型的函数State a -> List (State b).不幸的是,State类型是不透明的(非导出的),所以我们不能直接编写代码,但你可以定义自己的状态并使用它.

不要过多地执行细节,请记住它只是某种类型函数的包装器.所以问题可能是,为什么这样写呢?

好吧,实现使分析Parsers的方式更容易隐藏实现细节,提供原始解析器的良好基础,允许优雅的方式分层解析器而不必担心状态.在处理解析器和解码器时,或者在围绕状态的任何事物中,通常会在函数式语言中看到这种类型的模式.

阅读Haskell内部状态monad的介绍可能会有所帮助.类型不同,但许多基本概念是共享的.