简体   繁体   English

optparse-applicative 与自定义 monad

[英]optparse-applicative with custom monad

I'm trying to use my own monad (instead of IO ) with customExecParser https://hackage.haskell.org/package/optparse-applicative-0.15.1.0/docs/Options-Applicative-Extra.html#v:customExecParser .我正在尝试将我自己的 monad(而不是IO )与customExecParser https://hackage.haskell.org/package/optparse-applicative-0.15.1.0/docs/Options-Applicative-Extra.html#v:customExecParser 一起使用

So I've ended up with (significant function being fff ):所以我最终得到了(重要的功能是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" )

However, the only disadvantage with the above approach is needing to fmap the required number of times (matching the function arguments in xyzz or xyzz ).但是,上述方法的唯一缺点是需要fmap所需的次数(匹配xyzzxyzz的函数参数)。 I do recall running into this type of problem before.我确实记得以前遇到过这种类型的问题。 Is there some way I can avoid this (and just have a single function needing to be called)?有什么方法可以避免这种情况(并且只需要调用一个函数)?

Ideally I'd hope to have a monad transformer for this but unfortunately this seems to be implemented to IO only.理想情况下,我希望为此有一个 monad 转换器,但不幸的是,这似乎仅适用于IO

I think this boils down to the question: is there a function fff that can be applied to both of:我认为这归结为一个问题:是否有一个函数fff可以应用于:

xyzz  :: a -> r
xyzz' :: a -> b -> r

so that:以便:

fff xyzz  :: a -> r'
fff xyzz' :: a -> b -> r'

And the answer is "no", at least not without some type class trickery that isn't worth considering.答案是“不”,至少在没有一些不值得考虑的类型类技巧的情况下如此。

Instead, assuming your real version of fff doesn't actually do anything with f except compose with it, I guess I would consider writing:相反,假设您的真实版本fff实际上除了与f组合之外实际上并没有对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" ))

This whole approach seems a little "off", though.不过,这整个方法似乎有点“偏离”。 Do you really need a MoscConfig available while parsing options?解析选项时你真的需要一个可用的MoscConfig吗? Unless you have a really complicated options parsing problem on your hands, it would be more usual to parse the options directly into an intermediate data structure and then run your Mosco actions against that data structure to modify a MoscConfig state and do IO and so on.除非您手头有一个非常复杂的选项解析问题,否则更常见的是将选项直接解析为中间数据结构,然后针对该数据结构运行Mosco操作以修改MoscConfig状态并执行IO等。

In terms of what I wanted to achieve (being able to just pass parameters to function within the Mosco monad context -就我想要实现的目标而言(能够仅将参数传递给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" )
    )

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM