我有一个选项记录,我需要转换成命令行参数.
例如:
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语句的解决方案并不理想.
有没有解决这类问题的共同模式?
不,据我所知,没有真正的内置方法可以做到这一点.您可以尝试使用某种通用编程,但这可能不会很好地工作并且非常困难.相反,我会建议重组你的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)