在System.Console.GetOpt文档中给出的第二个示例中,我在这里转载,我无法理解或解压这一行:
(o,n,[] ) -> return (foldl (flip id) defaultOptions o, n)
这个foldl在做什么,它是如何实现的? (翻转id)的目的是什么?发生了什么事?
代码:
import System.Console.GetOpt
import Data.Maybe ( fromMaybe )
data Options = Options
{ optVerbose :: Bool
, optShowVersion :: Bool
, optOutput :: Maybe FilePath
, optInput :: Maybe FilePath
, optLibDirs :: [FilePath]
} deriving Show
defaultOptions = Options
{ optVerbose = False
, optShowVersion = False
, optOutput = Nothing
, optInput = Nothing
, optLibDirs = []
}
options :: [OptDescr (Options -> Options)]
options =
[ Option ['v'] ["verbose"]
(NoArg (\ opts -> opts { optVerbose = True }))
"chatty output on stderr"
, Option ['V','?'] ["version"]
(NoArg (\ opts -> opts { optShowVersion = True }))
"show version number"
, Option ['o'] ["output"]
(OptArg ((\ f opts -> opts { optOutput = Just f }) . fromMaybe "output")
"FILE")
"output FILE"
, Option ['c'] []
(OptArg ((\ f opts -> opts { optInput = Just f }) . fromMaybe "input")
"FILE")
"input FILE"
, Option ['L'] ["libdir"]
(ReqArg (\ d opts -> opts { optLibDirs = optLibDirs opts ++ [d] }) "DIR")
"library directory"
]
compilerOpts :: [String] -> IO (Options, [String])
compilerOpts argv =
case getOpt Permute options argv of
(o,n,[] ) -> return (foldl (flip id) defaultOptions o, n)
(_,_,errs) -> ioError (userError (concat errs ++ usageInfo header options))
where header = "Usage: ic [OPTION...] files..."
答案 0 :(得分:4)
flip id
的类型为b -> (b -> c) -> c
,您可以在此处找到解释:Why does Haskell's "flip id" has this type?
foldl (flip id) defaultOptions o
子表达式执行以下操作:
defaultOptions
作为初始值(defaultOptions
具有类型Options
)o
中取出每个元素(每个元素都有Options -> Options
} flip id
函数(它具有b -> (b -> c) -> c
类型)由于所有o
元素都会更改给定配置中的相应选项,因此foldl (flip id) defaultOptions o
的结果将是所有已解析选项的配置。所有错过的选项都替换为defaultOptions
的默认值。
(o,n,[] ) -> return (foldl (flip id) defaultOptions o, n)
表达式的其他部分非常简单:
(o,n,[] ) ->
匹配已解析选项列表,非选项列表和错误列表return (..., n)
只需将值(..., n)
置于monad IO (Options, [String])
答案 1 :(得分:2)
(这不是你问题的严格答案,但@soon要求我发布它。)
您定义的每个命令行参数的语义由描述
过渡函数Options -> Options
。既然你可以通过很多
程序的参数,最终得到这样的转换列表
函数[Options -> Options]
。目标是计算总和
这些过渡的影响,即适用的Options -> Options
每个过渡轮流。
实现这一目标的一个特别好的方法是观察结构
对于任何类型a -> a
:
a
id :: a -> a
是身份转换函数,实际上并没有做任何事情
给出两个转换函数f1, f2 :: a -> a
,它们的组成
f1 . f2
恰好对应于按顺序应用两者。注意
同样,这个组合是关联的,因为做f2 . f3
和。{
然后f1
与f3
后跟f1 . f2
相同。
所以我们有一个幺半群!
Haskell标准库base
已经包含了这个monoid
名称Endo
。
使用它,我们可以重写
foldl (flip id) defaultOptions o
以更好的方式,在我看来,这使得它立即显现在这里发生了什么:
appEndo (fold o) defaultOptions
将options
的结果类型更改为[OptDescr (Endo Options)]
;或者,如果您不愿意,可以添加额外的Endo
组合时的线噪声(通过写appEndo (foldMap Endo o) defaultOptions
)。在这里,fold o :: Endo Options
是所有单个转换函数的合成,appEndo (fold o) :: Options -> Options
是最终将此结果转换函数应用于初始Options
的方式。
请注意,无论用于o
的数据结构如何,这都有效:它适用于转换函数列表,树或Maybe
,因为它的关联属性手上的幺半群;而fold
的多态性足以揭示这一点。