我该如何将选项记录转换为字符串列表?

Joe*_*and 1 haskell

我有一个选项记录,我需要转换成命令行参数.

例如:

data Options = Options { optFoo :: Maybe Int
                       , optYes :: Bool
                       , optBar :: Maybe String
                       }

options = Options { optFoo = Just 3
                  , optYes = True
                  , optBar = Nothing
                  }

callThing :: Options -> IO ()
callThing opts = do
    callProcess "/usr/bin/thing" $ optsToArgs opts

-- Output should be: ["--foo", "3", "-y"]
optsToArgs :: Options -> [String]
optsToArgs opts = ???
Run Code Online (Sandbox Code Playgroud)

我想象能够使用List Monad,但我无法弄清楚如何让它工作.

在我的特定情况下,Options中有大约20种不同的东西,因此使用嵌套的if/case语句的解决方案并不理想.

有没有解决这类问题的共同模式?

bhe*_*ilr 5

不,据我所知,没有真正的内置方法可以做到这一点.您可以尝试使用某种通用编程,但这可能不会很好地工作并且非常困难.相反,我会建议重组你的Options类型:

data Option
    = Foo Int
    | Yes
    | Bar String
    deriving (Eq, Show)

type Options = [Option]

optToArgs :: Option -> [String]
optToArgs opt = case opt of
    Foo n -> ["--foo", show n]
    Yes   -> ["-y"]
    Bar s -> ["--bar", s]

optsToArgs :: Options -> [String]
optsToArgs = concatMap optToArgs
Run Code Online (Sandbox Code Playgroud)

然后你会的

> optsToArgs [Foo 3, Yes]
["--foo", "3", "-y"]
> optsToArgs [Yes, Bar "test"]
["-y", "--bar", "test"]
Run Code Online (Sandbox Code Playgroud)

这样,每个选项都有不同的构造函数.从单个选项到其相应参数的转换在一个地方处理,然后将多个选项转换为参数列表很简单.如果需要,这还允许您具有更多层次结构的选项结构:

data FooOption
    = Foo1 Int
    | Foo2 Double

data BarOption
    = Bar1 String
    = Bar2 (String, String)

class Opt o where
    toArgs :: o -> [String]

instance Opt FooOption where
    toArgs (Foo1 n) = ["--foo", show n]
    toArgs (Foo2 d) = ["--foo", show d]

instance Opt BarOption where
    toArgs (Bar1 s) = ["--bar", s]
    toArgs (Bar2 (s1, s2)) = ["--bar", s1, "--bar", s2]

data Option
    = Foo FooOption
    | Bar BarOption
    | Yes

instance Opt Option where
    toArgs (Foo f) = toArgs f
    toArgs (Bar b) = toArgs b
    toArgs Yes     = ["-y"]

type Options = [Option]

instance Opt o => Opt [o] where
    toArgs = concatMap toArgs
Run Code Online (Sandbox Code Playgroud)

那你就得到了

callThing = callProcess "/usr/bin/thing" . toArgs
Run Code Online (Sandbox Code Playgroud)

举个例子:

> toArgs [Bar $ Bar2 ("hello", "world"), Foo $ Foo1 3, Yes]
["--bar", "hello", "--bar", "world", "--foo", "3", "-y"]
Run Code Online (Sandbox Code Playgroud)