我有一个选项记录,我需要转换成命令行参数。
例如:
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 = ???
我想象能够使用List Monad,但我无法弄清楚如何让它工作。
在我的特定情况下,Options中有大约20种不同的东西,因此使用嵌套的if / case语句的解决方案并不理想。
是否有解决此类问题的常见模式?
答案 0 :(得分: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
然后你会
> optsToArgs [Foo 3, Yes]
["--foo", "3", "-y"]
> optsToArgs [Yes, Bar "test"]
["-y", "--bar", "test"]
这样,每个选项都有不同的构造函数。从单个选项到其相应参数的转换在一个地方处理,然后将多个选项转换为参数列表很简单。如果需要,这还可以让您拥有更多层次结构的选项结构:
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
然后你就有了
callThing = callProcess "/usr/bin/thing" . toArgs
举个例子:
> toArgs [Bar $ Bar2 ("hello", "world"), Foo $ Foo1 3, Yes]
["--bar", "hello", "--bar", "world", "--foo", "3", "-y"]
答案 1 :(得分:1)
我会创建一个TypeClass,它将选项的名称作为参数。
class ToCommand a where
toCmd :: a -> String -> [String]
instance ToCommand Int where
toCmd i n = [n,show i]
instance ToCommand Bool where
toCmd True n = [n]
toCmd False _ = []
instance (ToCommand a)=> ToCommand (Maybe a) where
toCmd Nothing _ = []
toCmd (Just a) n = toCmd a n
使用RecordWildcards,你可以做到
optToArgs opts{..} = concat (toCmd optFoo "--foo") (toCmd optYes "-y") ...
也许更通用的解决方案可行,但实施起来会更复杂。