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

Haskell 如何将选项记录转换为字符串列表?,haskell,Haskell,我有一个需要转换为命令行参数的选项记录 例如: data Options=Options{optFoo::Maybe Int ,optYes::Bool ,optBar::可能是字符串 } 选项=选项{optFoo=3 ,opties=True ,optBar=Nothing } 调用:选项->IO() callThing opts=do callProcess“/usr/bin/thing”$optsToArgs opts --输出应为:[“--foo”,“3”,“-y”] optsToArg

我有一个需要转换为命令行参数的选项记录

例如:

data Options=Options{optFoo::Maybe Int
,optYes::Bool
,optBar::可能是字符串
}
选项=选项{optFoo=3
,opties=True
,optBar=Nothing
}
调用:选项->IO()
callThing opts=do
callProcess“/usr/bin/thing”$optsToArgs opts
--输出应为:[“--foo”,“3”,“-y”]
optsToArgs::选项->[字符串]
optsToArgs opts=???
我想象着可以使用列表单子,但我不知道如何使它工作

在我的具体案例中,Options包含大约20种不同的内容,因此使用嵌套if/case语句的解决方案并不理想


有没有解决这类问题的通用模式?

不,据我所知,没有真正的内置方法来解决这类问题。您可以尝试使用某种泛型编程,但这可能不会很好地工作,而且相当困难。相反,我建议重组您的
选项
类型:

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"]

不,据我所知,没有一个真正的内在方法来做到这一点。您可以尝试使用某种泛型编程,但这可能不会很好地工作,而且相当困难。相反,我建议重组您的
选项
类型:

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"]

我将创建一个以选项名称作为参数的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
使用记录通配符,您可以

 optToArgs opts{..} = concat (toCmd optFoo "--foo") (toCmd optYes "-y") ...

也许一个更通用的解决方案会起作用,但实施起来会更复杂。

我会创建一个以选项名称为参数的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
使用记录通配符,您可以

 optToArgs opts{..} = concat (toCmd optFoo "--foo") (toCmd optYes "-y") ...

也许可以使用更通用的解决方案,但实施起来会更复杂。

如果“仅”将它们用作命令行参数,请使用已经存在的优秀库之一:。我已经成功地使用了optpass applicative和cmdargs。@JPMoresmau我的方案是另一个方向。我正在创建一个通过命令行进行抽象的库,因此命令行解析器在这种情况下没有帮助。哦,对不起,是的,我在您的代码中看到。如果“仅”将它们用作命令行参数,请使用已经存在的一个优秀库:。我已经成功地使用了optpass applicative和cmdargs。@JPMoresmau我的方案是另一个方向。我正在创建一个通过命令行进行抽象的库,因此命令行解析器在这种情况下没有帮助。哦,对不起,是的,我在你的代码中看到了我喜欢这个解决方案,但不幸的是,我只能选择一个答案。我选择了另一个,因为它更简单,并且直接适合我的场景,尽管我将尝试更改我的选项类型,看看我是否更喜欢它。我喜欢这个解决方案,但不幸的是,我只能选择一个答案。我选择了另一个,因为它更简单,并且直接适合我的场景,不过我将尝试更改我的选项类型,看看我是否更喜欢它。