如何正确使用 Terminfo 的 Capability 类型。 monad 转换器是答案吗?

How to use Terminfo's Capability type correctly. Are monad transformers the answer?

我一直在努力使用 System.Console.Terminfo 来简化一个小程序。我已经 mappend 将多个 Capability 放在一起,但是每当我需要评估它们时,我都必须使用 getCapability 然后使用 case 进行模式化-匹配结果 Maybe。图案总是一样

Just ... -> runTermOutput ...
Nothing -> return ()

所以我认为一定有更好的方法来做到这一点。在我看来,模式匹配正在用 IO 替换 Maybe,所以我认为这可能是 monad 转换器的用途。查看 Capability 定义,

> :i Capability
newtype Capability a
  = System.Console.Terminfo.Base.Capability (Terminal
                                             -> IO (Maybe a))
...

它看起来确实与我在 Whosebug 上找到的 MaybeT 示例相似,但它是一个函数这一事实让我很反感。 (另外,我不能声称只读了一个例子就理解了 monad 转换器。)

我走在正确的轨道上吗?有没有不同的模式可以帮助我避免一遍又一遍地写这个 case

这里是 getCapability 类型:

> :i getCapability 
getCapability :: Terminal -> Capability a -> Maybe a
...
mapM :: (Monad m, Traversable t) => (a -> m b) -> t a -> m (t b)

不仅专注于

mapM :: (a -> IO b) -> [a] -> IO [b]

,还要

mapM :: (a -> IO b) -> Maybe a -> IO (Maybe b)

mapM_forfor_traversetraverse_

也是如此

据我了解,Terminfo Capability 接口意味着 monad 转换器不是答案。

正如 user2407038 所建议的,case 模式可以通过函数避免,

\t -> maybe (return ()) (runTermOutput t) . (getCapability t)
  :: Terminal -> Capability TermOutput -> IO ()