为什么 newtype 语法创建一个函数

Why the newtype syntax creates a function

我看这个声明:

newtype Parser a = Parser { parse :: String -> Maybe (a,String) }

这是我的理解:

1) 解析器被声明为带有类型参数的类型 a

2) 你可以通过提供解析器函数来实例化Parser,例如p = Parser (\s -> Nothing)

我观察到的是,我突然定义了一个函数名称 parse,它能够 运行 解析器。

比如我可以运行:

parse (Parser  (\s -> Nothing)) "my input" 

并得到 Nothing 作为输出。

这个解析函数是如何用这个特定的签名定义的?这个函数 "know" 是如何执行给它的 Parser 的呢?希望有人能解开我的困惑。

谢谢!

首先,让我们看一下没有记录语法的解析器新类型:

newtype Parser' a = Parser' (String -> Maybe (a,String))

这个类型的作用应该很明显:它存储一个函数 String -> Maybe (a,String)。为了 运行 这个解析器,我们需要创建一个新函数:

runParser' :: Parser' -> String -> Maybe (a,String)
runParser' (Parser' p) i = p i

现在我们可以 运行 解析器,例如 runParser' (Parser' $ \s -> Nothing) "my input"

但现在请注意,由于 Haskell 函数是柯里化的,我们可以简单地删除对输入 i 的引用以获得:

runParser'' :: Parser' -> (String -> Maybe (a,String))
runParser'' (Parser' p) = p

这个函数完全等同于runParser',但你可以换个方式思考:它不是将解析器函数显式地应用到值,而是简单地接受一个解析器并从中获取解析器函数;但是,由于柯里化,runParser'' 仍然可以与两个参数一起使用。

现在,让我们回到原来的类型:

newtype Parser a = Parser { parse :: String -> Maybe (a,String) }

你的类型和我的唯一区别是你的类型使用record syntax,虽然它可能有点难以识别,因为newtype只能有一个字段;此记录语法自动定义一个函数 parse :: Parser a -> (String -> Maybe (a,String)),它从 Parser a 中提取 String -> Maybe (a,String) 函数。希望其余部分应该是显而易见的:由于柯里化,parse 可以与两个参数而不是一个参数一起使用,这只是具有 运行 存储在 Parser a 中的函数的效果。换句话说,您的定义完全等同于以下代码:

newtype Parser a = Parser (String -> Maybe (a,String))

parse :: Parser a -> (String -> Maybe (a,String))
parse (Parser p) = p

当你写 newtype Parser a = Parser { parse :: String -> Maybe (a,String) } 时,你引入了三件事:

  1. 一个名为 Parser.

  2. 的类型
  3. Parser 的术语级构造函数,名为 Parser。这个函数的类型是

Parser :: (String -> Maybe (a, String)) -> Parser a

你给它一个函数,它把它包装在一个 Parser

  1. 一个名为 parse 的函数,用于删除 Parser 包装器并取回您的函数。这个函数的类型是:
parse :: Parser a -> String -> Maybe (a, String)

检查自己 ghci:

Prelude> newtype Parser a = Parser { parse :: String -> Maybe (a,String) }
Prelude> :t Parser
Parser :: (String -> Maybe (a, String)) -> Parser a
Prelude> :t parse
parse :: Parser a -> String -> Maybe (a, String)
Prelude>

术语级别构造函数(Parser)和移除包装器的函数(parse)都是任意名称,不需要匹配类型名称,这毫无意义。例如,通常写为:

newtype Parser a = Parser { unParser :: String -> Maybe (a,String) }

这很清楚 unParse 删除了解析函数周围的包装器。但是,我建议您的类型和构造函数在使用 newtypes.

时具有相同的名称

How does this function "know" to execute the Parser given to it

您正在使用 parse 展开函数,然后使用 "myInput" 调用展开的函数。