如何在 Haskell 中手动部分应用函数
How to manually partially apply a function in Haskell
我无法理解以下代码的工作原理。我正在尝试遵循找到的解析器示例 here。我之前使用过ReadP
,想更深入地了解解析器的工作原理。我的问题不特定于解析器,而是特定于 Haskell 中的函数应用程序如何工作。这是代码:
module NanoParsec where
import Data.Char
import Control.Monad
import Control.Applicative
newtype Parser a = Parser { parse :: String -> [(a,String)] }
runParser :: Parser a -> String -> a
runParser m s =
case parse m s of
[(res, [])] -> res
[(_, rs)] -> error "Parser did not consume entire stream."
_ -> error "Parser error."
item :: Parser Char
item = Parser $ \s ->
case s of
[] -> []
(c:cs) -> [(c,cs)]
我知道还不需要导入。当我将其加载到 ghci
时,它按预期工作:
Prelude> runParser item "m"
'm'
Prelude> runParser item "ms"
*** Exception: Parser did not consume entire stream.
所以这里不足为奇。我无法理解的是 runParser
是如何应用于 item
的。将 runParser
部分应用于 item
会产生类型为 String -> Char
:
的函数
Parser> :t runParser item
runParser item :: String -> Char
以下是我尝试手动 desugar/apply runParser
到 item
的方式:
runParser item
runParser Parser (\s -> ...)
(\m -> \s -> case parse m s of ...) Parser (\s -> ...)
这就是我卡住的地方。如果我用 Parser
中的函数替换 m
,我会得到 parse parse s
,这对我来说似乎是错误的。如何将 runParser
应用到 item
以便最终得到类型为 String -> Char
的函数?我觉得我的 attempt at desugaring 没电了。
编辑:我错误地指出 item
应用于 runParser
;我在这里混淆了函数和变量。
也许这会让您朝着正确的方向开始:
runParser item
= { definition of item }
runParser (Parser $ \s -> case s of ...)
= { definition of runParser }
(\m s -> case parse m s of ...) (Parser $ \s -> case s of ...)
= { substitute argument for m everywhere }
(\s -> case parse (Parser $ \s -> case s of ...) s of ...)
= { definition of parse }
(\s -> case (\(Parser f) -> f) (Parser $ \s -> case s of ...) s of ...)
= { substitute argument for f everywhere }
(\s -> case (\s -> case s of ...) s of ...)
= { substitute argument for s everywhere }
(\s -> case (case s of ...) of ...)
我稍微修改了我的代码,它使用模式匹配而不是记录语法。这对其他人来说可能更容易理解:
module NanoParsec where
import Data.Char
import Control.Monad
import Control.Applicative
-- newtype Parser a = Parser { parse :: String -> [(a,String)] }
newtype Parser a = Parser (String -> [(a,String)])
runParser :: Parser a -> String -> a
runParser (Parser p) s =
case p s of
[(res, [])] -> res
[(_, rs)] -> error "Parser did not consume entire stream."
_ -> error "Parser error."
item :: Parser Char
item = Parser $ \s ->
case s of
[] -> []
(c:cs) -> [(c,cs)]
我无法理解以下代码的工作原理。我正在尝试遵循找到的解析器示例 here。我之前使用过ReadP
,想更深入地了解解析器的工作原理。我的问题不特定于解析器,而是特定于 Haskell 中的函数应用程序如何工作。这是代码:
module NanoParsec where
import Data.Char
import Control.Monad
import Control.Applicative
newtype Parser a = Parser { parse :: String -> [(a,String)] }
runParser :: Parser a -> String -> a
runParser m s =
case parse m s of
[(res, [])] -> res
[(_, rs)] -> error "Parser did not consume entire stream."
_ -> error "Parser error."
item :: Parser Char
item = Parser $ \s ->
case s of
[] -> []
(c:cs) -> [(c,cs)]
我知道还不需要导入。当我将其加载到 ghci
时,它按预期工作:
Prelude> runParser item "m"
'm'
Prelude> runParser item "ms"
*** Exception: Parser did not consume entire stream.
所以这里不足为奇。我无法理解的是 runParser
是如何应用于 item
的。将 runParser
部分应用于 item
会产生类型为 String -> Char
:
Parser> :t runParser item
runParser item :: String -> Char
以下是我尝试手动 desugar/apply runParser
到 item
的方式:
runParser item
runParser Parser (\s -> ...)
(\m -> \s -> case parse m s of ...) Parser (\s -> ...)
这就是我卡住的地方。如果我用 Parser
中的函数替换 m
,我会得到 parse parse s
,这对我来说似乎是错误的。如何将 runParser
应用到 item
以便最终得到类型为 String -> Char
的函数?我觉得我的 attempt at desugaring 没电了。
编辑:我错误地指出 item
应用于 runParser
;我在这里混淆了函数和变量。
也许这会让您朝着正确的方向开始:
runParser item
= { definition of item }
runParser (Parser $ \s -> case s of ...)
= { definition of runParser }
(\m s -> case parse m s of ...) (Parser $ \s -> case s of ...)
= { substitute argument for m everywhere }
(\s -> case parse (Parser $ \s -> case s of ...) s of ...)
= { definition of parse }
(\s -> case (\(Parser f) -> f) (Parser $ \s -> case s of ...) s of ...)
= { substitute argument for f everywhere }
(\s -> case (\s -> case s of ...) s of ...)
= { substitute argument for s everywhere }
(\s -> case (case s of ...) of ...)
我稍微修改了我的代码,它使用模式匹配而不是记录语法。这对其他人来说可能更容易理解:
module NanoParsec where
import Data.Char
import Control.Monad
import Control.Applicative
-- newtype Parser a = Parser { parse :: String -> [(a,String)] }
newtype Parser a = Parser (String -> [(a,String)])
runParser :: Parser a -> String -> a
runParser (Parser p) s =
case p s of
[(res, [])] -> res
[(_, rs)] -> error "Parser did not consume entire stream."
_ -> error "Parser error."
item :: Parser Char
item = Parser $ \s ->
case s of
[] -> []
(c:cs) -> [(c,cs)]