首页 > 解决方案 > optparse-applicative 与自定义 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需要所需的次数(匹配xyzzor中的函数参数xyzz)。我确实记得以前遇到过这类问题。有什么方法可以避免这种情况(并且只需要调用一个函数)?

理想情况下,我希望为此有一个 monad 转换器,但不幸的是,这似乎只能实现IO

标签: haskelloptparse-applicative

解决方案


我认为这归结为一个问题:是否有一个功能fff可以应用于两者:

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

以便:

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

答案是“不”,至少没有一些不值得考虑的类型类诡计。

相反,假设您的真实版本除了 compose 之外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" ))

不过,这整个方法似乎有点“过时”。解析选项时你真的需要一个MoscConfig可用的选项吗?除非您手头有一个非常复杂的选项解析问题,否则通常将选项直接解析为中间数据结构,然后针对该数据结构运行您的操作以修改状态并执行等等。MoscoMoscConfigIO


推荐阅读