GetOpt用法和foldl,flip,id

时间:2015-09-02 02:06:39

标签: haskell

在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..."

2 个答案:

答案 0 :(得分:4)

flip id的类型为b -> (b -> c) -> c,您可以在此处找到解释:Why does Haskell's "flip id" has this type?

foldl (flip id) defaultOptions o子表达式执行以下操作:

  1. defaultOptions作为初始值(defaultOptions具有类型Options
  2. o中取出每个元素(每个元素都有Options -> Options}
  3. 使用flip id函数(它具有b -> (b -> c) -> c类型)
  4. 折叠所有元素

    由于所有o元素都会更改给定配置中的相应选项,因此foldl (flip id) defaultOptions o的结果将是所有已解析选项的配置。所有错过的选项都替换为defaultOptions的默认值。

    (o,n,[] ) -> return (foldl (flip id) defaultOptions o, n)表达式的其他部分非常简单:

    1. (o,n,[] ) ->匹配已解析选项列表,非选项列表和错误列表
    2. return (..., n)只需将值(..., n)置于monad IO (Options, [String])

答案 1 :(得分:2)

(这不是你问题的严格答案,但@soon要求我发布它。)

您定义的每个命令行参数的语义由描述 过渡函数Options -> Options。既然你可以通过很多 程序的参数,最终得到这样的转换列表 函数[Options -> Options]。目标是计算总和 这些过渡的影响,即适用的Options -> Options 每个过渡轮流。

实现这一目标的一个特别好的方法是观察结构 对于任何类型a -> a

的endomorphisms a
  • id :: a -> a是身份转换函数,实际上并没有做任何事情

  • 给出两个转换函数f1, f2 :: a -> a,它们的组成 f1 . f2恰好对应于按顺序应用两者。注意 同样,这个组合是关联的,因为做f2 . f3和。{ 然后f1f3后跟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的多态性足以揭示这一点。