我仍然是Haskell的新手,请原谅我,如果这是完全明显的,我只是没有正确理解。
On Hackage the documentation说System.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实例记录。
感谢所有人帮助我思考这一切!
答案 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}}来实现相同目标。