带有自定义 monad 的 optparse-applicative
optparse-applicative with custom monad
我正在尝试将我自己的 monad(而不是 IO
)用于 customExecParser
https://hackage.haskell.org/package/optparse-applicative-0.15.1.0/docs/Options-Applicative-Extra.html#v:customExecParser。
所以我得到了(重要的功能是 fff
):
data MoscConfig = MoscConfig {
datadir :: FilePath
, config :: FilePath
, pendingPath :: FilePath
, socket :: FilePath
}
type Mosco = StateT MoscConfig IO
main :: IO ()
main = join . customExecParser (prefs showHelpOnError) $
info (helper <*> parser)
( fullDesc
)
fff :: (a1 -> StateT MoscConfig IO a2) -> a1 -> IO a2
fff f = (flip evalStateT (MoscConfig "" "" "" "")) . f
xyzz :: Text -> Mosco ()
xyzz x = do
liftIO $ print x
liftIO $ print "testabcxyz"
xyzz' :: Text -> Text -> Mosco ()
xyzz' x x' = do
liftIO $ print x
liftIO $ print x'
liftIO $ print "testabcxyz"
parser :: Parser (IO ())
parser = do
fff xyzz <$> textOption ( long "zzz" )
<|>
((fmap fff) xyzz')
<$> textOption ( long "zzz" )
<*> textOption ( long "zzz" )
然而,上述方法的唯一缺点是需要 fmap
所需的次数(匹配 xyzz
或 xyzz
中的函数参数)。我记得 运行 以前遇到过这类问题。有什么方法可以避免这种情况(只需要调用一个函数)?
理想情况下,我希望为此有一个 monad 转换器,但不幸的是,这似乎只适用于 IO
。
我认为这可以归结为一个问题:是否有一个函数 fff
可以同时应用于:
xyzz :: a -> r
xyzz' :: a -> b -> r
这样:
fff xyzz :: a -> r'
fff xyzz' :: a -> b -> r'
答案是 "no",至少不是没有一些不值得考虑的 class 诡计。
相反,假设你的 fff
的真实版本除了与它组合之外实际上没有对 f
做任何事情,我想我会考虑写:
fff :: Parser (Mosco a) -> Parser (IO a)
fff = fmap $ flip evalStateT (MoscConfig "" "" "" "")
parser :: Parser (IO ())
parser = fff (xyzz <$> textOption ( long "zzz" ))
<|> fff (xyzz' <$> textOption ( long "zzz" ) <*> textOption ( long "zzz" ))
虽然整个方法看起来有点 "off"。您 真的 需要 MoscConfig
在 解析 选项时可用吗?除非你手上有一个非常复杂的选项解析问题,否则更常见的做法是将选项直接解析为中间数据结构,然后 运行 你对该数据结构的 Mosco
操作来修改 MoscConfig
声明并执行 IO
等等。
就我想要实现的目标而言(能够仅将参数传递给 Mosco
monad 上下文中的函数 -
moscparams ::
Maybe Text
-> Maybe Text
-> Maybe Text
-> Maybe Text
-> Mosco a -> IO a
moscparams dd c pp sp x = do
ddd <- crFile
cd <- pure "not used"
ppd <- crDirPending
spd <- socketFile
evalStateT x
$ MoscConfig
(maybe ddd cs dd)
(maybe cd cs c)
(maybe ppd cs pp)
(maybe spd cs sp)
moscF' :: Text -> Text -> Mosco ()
moscF' x x' = do
liftIO $ print x
liftIO $ print x'
liftIO $ print "testabcxyz"
moscparams' :: Parser (Mosco ()) -> Parser (IO ())
moscparams' x = moscparams
<$> optional (textOption ( long "data-dir" ))
<*> optional (textOption ( long "config-path" ))
<*> optional (textOption ( long "pending-path" ))
<*> optional (textOption ( long "socket-path" ))
<*> x
parser :: Parser (IO ())
parser = do
moscparams'
(( moscF')
<$> textOption ( long "example-param-1" )
<*> textOption ( long "example-param-2" )
)
我正在尝试将我自己的 monad(而不是 IO
)用于 customExecParser
https://hackage.haskell.org/package/optparse-applicative-0.15.1.0/docs/Options-Applicative-Extra.html#v:customExecParser。
所以我得到了(重要的功能是 fff
):
data MoscConfig = MoscConfig {
datadir :: FilePath
, config :: FilePath
, pendingPath :: FilePath
, socket :: FilePath
}
type Mosco = StateT MoscConfig IO
main :: IO ()
main = join . customExecParser (prefs showHelpOnError) $
info (helper <*> parser)
( fullDesc
)
fff :: (a1 -> StateT MoscConfig IO a2) -> a1 -> IO a2
fff f = (flip evalStateT (MoscConfig "" "" "" "")) . f
xyzz :: Text -> Mosco ()
xyzz x = do
liftIO $ print x
liftIO $ print "testabcxyz"
xyzz' :: Text -> Text -> Mosco ()
xyzz' x x' = do
liftIO $ print x
liftIO $ print x'
liftIO $ print "testabcxyz"
parser :: Parser (IO ())
parser = do
fff xyzz <$> textOption ( long "zzz" )
<|>
((fmap fff) xyzz')
<$> textOption ( long "zzz" )
<*> textOption ( long "zzz" )
然而,上述方法的唯一缺点是需要 fmap
所需的次数(匹配 xyzz
或 xyzz
中的函数参数)。我记得 运行 以前遇到过这类问题。有什么方法可以避免这种情况(只需要调用一个函数)?
理想情况下,我希望为此有一个 monad 转换器,但不幸的是,这似乎只适用于 IO
。
我认为这可以归结为一个问题:是否有一个函数 fff
可以同时应用于:
xyzz :: a -> r
xyzz' :: a -> b -> r
这样:
fff xyzz :: a -> r'
fff xyzz' :: a -> b -> r'
答案是 "no",至少不是没有一些不值得考虑的 class 诡计。
相反,假设你的 fff
的真实版本除了与它组合之外实际上没有对 f
做任何事情,我想我会考虑写:
fff :: Parser (Mosco a) -> Parser (IO a)
fff = fmap $ flip evalStateT (MoscConfig "" "" "" "")
parser :: Parser (IO ())
parser = fff (xyzz <$> textOption ( long "zzz" ))
<|> fff (xyzz' <$> textOption ( long "zzz" ) <*> textOption ( long "zzz" ))
虽然整个方法看起来有点 "off"。您 真的 需要 MoscConfig
在 解析 选项时可用吗?除非你手上有一个非常复杂的选项解析问题,否则更常见的做法是将选项直接解析为中间数据结构,然后 运行 你对该数据结构的 Mosco
操作来修改 MoscConfig
声明并执行 IO
等等。
就我想要实现的目标而言(能够仅将参数传递给 Mosco
monad 上下文中的函数 -
moscparams ::
Maybe Text
-> Maybe Text
-> Maybe Text
-> Maybe Text
-> Mosco a -> IO a
moscparams dd c pp sp x = do
ddd <- crFile
cd <- pure "not used"
ppd <- crDirPending
spd <- socketFile
evalStateT x
$ MoscConfig
(maybe ddd cs dd)
(maybe cd cs c)
(maybe ppd cs pp)
(maybe spd cs sp)
moscF' :: Text -> Text -> Mosco ()
moscF' x x' = do
liftIO $ print x
liftIO $ print x'
liftIO $ print "testabcxyz"
moscparams' :: Parser (Mosco ()) -> Parser (IO ())
moscparams' x = moscparams
<$> optional (textOption ( long "data-dir" ))
<*> optional (textOption ( long "config-path" ))
<*> optional (textOption ( long "pending-path" ))
<*> optional (textOption ( long "socket-path" ))
<*> x
parser :: Parser (IO ())
parser = do
moscparams'
(( moscF')
<$> textOption ( long "example-param-1" )
<*> textOption ( long "example-param-2" )
)