Haskell的System.Console.GetOpt ReqArg如何将2 arity函数作为构造函数的第一个参数?

时间:2015-12-08 19:41:29

标签: haskell getopt

我仍然是Haskell的新手,请原谅我,如果这是完全明显的,我只是没有正确理解。

On Hackage the documentationSystem.Console.GetOpt ReqArg采用arity 1的函数,例如(String -> a)作为其构造函数的第一个参数。

ReqArg (String -> a) String

在我看到的许多示例中,将2个arity函数传递给此构造函数。

https://wiki.haskell.org/High-level_option_handling_with_GetOpt)的例子:

data Options = Options  { optVerbose    :: Bool
                        , optInput      :: IO String
                        , optOutput     :: String -> IO ()
                        }

startOptions :: Options
startOptions = Options  { optVerbose    = False
                        , optInput      = getContents
                        , optOutput     = putStr
                        }

options :: [ OptDescr (Options -> IO Options) ]
options =
    [ Option "i" ["input"]
        (ReqArg
            (\arg opt -> return opt { optInput = readFile arg })
            "FILE")
        "Input file"

    , Option "o" ["output"]
        (ReqArg
            (\arg opt -> return opt { optOutput = writeFile arg })
            "FILE")
        "Output file"

    , Option "s" ["string"]
        (ReqArg
            (\arg opt -> return opt { optInput = return arg })
            "FILE")
        "Input string"

    , Option "v" ["verbose"]
        (NoArg
            (\opt -> return opt { optVerbose = True }))
        "Enable verbose messages"

    , Option "V" ["version"]
        (NoArg
            (\_ -> do
                hPutStrLn stderr "Version 0.01"
                exitWith ExitSuccess))
        "Print version"

    , Option "h" ["help"]
        (NoArg
            (\_ -> do
                prg <- getProgName
                hPutStrLn stderr (usageInfo prg options)
                exitWith ExitSuccess))
        "Show help"
    ]

所以我的问题是,当函数在其参数中使用时,值构造函数是不是真正强制执行类型,还是我还缺少其他东西?

更新

这对我来说更有意义。我相信我忽略了一些因素。首先,正如@CommuSoft所提到的,由于讨论,所有函数在Haskell中都是一个单一的arity。其次,我没有仔细研究那些不是函数的选项,而是一个类型为的变量:

[ OptDescr (Options -> IO Options) ]

这种类型的选项签名声明了ReqArg的类型变量的类型以及其他类型构造函数NoArg和OptArg(后者在示例中未使用)。

传递给NoArg ArgDescr构造函数的单个arity匿名函数基本上只是:

(Options -> IO Options)

例如,它将收到选项实例记录

传递给ReqArg构造函数的2 arity匿名函数将是:

(String -> Options -> IO Options)

它将收到一个字符串(某人在命令行输入的值)和Options实例记录。

感谢所有人帮助我思考这一切!

2 个答案:

答案 0 :(得分:4)

您在类型签名中看到的->实际上也是一种类型。因此,类型变量a可以是函数b -> c。在您的示例中,它是Options -> IO Options

答案 1 :(得分:3)

ReqArg不是函数:它是一个构造函数。现在构造函数显然也起作用。 ReqArg的签名是:

ReqArg :: (String -> a) -> String -> ArgDescr a

因此构造函数返回ArgDescr a

现在您必须注意的第二个方面是a在这种情况下等同于a = Options -> IO Options,这意味着您的ReqArg构造函数的签名会折叠为:

ReqArg :: (String -> (Options -> IO Options)) -> String -> ArgDescr (Options -> IO Options)

或更少噪音:

ReqArg :: (String -> Options -> IO Options) -> String -> ArgDescr (Options -> IO Options)

(括号已删除)

所以它是一个带有“ arity ”2的函数(请注意,严格来说,在函数式编程中,每个函数都有(至少在概念上)arity 1)。关键是你从第一个参数生成一个新函数。但是Haskells的语法糖允许同时“定义两个参数”。

解释文档

这就是为什么在文档示例中,您需要使用foldr

return (foldl (flip id) defaultOptions o, n)

请注意,这不会映射到您的Options -> IO Options,在示例中使用Options -> Options

关键是,在文档中,Haskell单独处理每个命令选项。最初,您从defaultOptions开始,并使用o中的n处理选项会产生新的Option,您将其用作处理下一个的Options输入。完成元素链后,您将返回最终的IO数据。

适用于您的计划

使用main Monad会让事情变得更难:无论是否必须打印版本,最好存储布尔值,如果是这种情况,请在foldl中执行此操作, 或者别的地方。不过,您可以使用foldlM代替{{1}}来实现相同目标。