Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use Haskell `do` notation with multiple monad constraints

I'm still quite new to Haskell and don't have a great grasp of monads intuitively. I'm trying to write the function below:

applyPandocFilters :: (MonadIO m, PandocMonad m) => Pandoc -> m Pandoc
applyPandocFilters = do
  luaEngine <- getEngine
  applyFilters
    luaEngine
    Environment{envReaderOptions = readerOptions, envWriterOptions = writerOptions}
    [ LuaFilter "./filters/lua/filters.lua"
    , CiteprocFilter
    ]
    []

The constraint (MonadIO m, PandocMonad m) comes from the function

applyFilters :: (PandocMonad m, MonadIO m)
=> ScriptingEngine
-> Environment
-> [Filter]
-> [String]
-> Pandoc
-> m Pandoc

which I'm partially applying with all but the last two arguments filled. The issue I'm struggling with is how to use the function getEngine :: (MonadIO m) => m ScriptingEngine inside the monadic context. The way I understand the problem is that a do block only applies to a single concrete monad at a time, and the monad which the return values of getEngine and applyFilters are in are different, which is preventing them from being sequenced in a single do block, but I had thought that since getEngine's return type was wrapped in one of the monads already expressed as a typeclass constraint on the function, I would be able to use its value in applyFilters, but I get the following error, which I haven't been able to understand.

• Could not deduce ‘MonadIO ((->) Pandoc)’
    arising from a use of ‘getEngine’
  from the context: (MonadIO m, PandocMonad m)
    bound by the type signature for:
               applyPandocFilters :: forall (m :: * -> *).
                                     (MonadIO m, PandocMonad m) =>
                                     Pandoc -> m Pandoc

The problem I'm ultimately trying to solve is just how to transform a Pandoc document using a lua filter called from within a haskell program, for which Pandoc provides the function applyFilters, which requires a ScriptingEngine. If there's another way to apply a filter without the use of the applyFilters function, that would work as well.

I've tried reading more about MonadIO and monad transformers, but I'm not entirely sure how I would apply them to this problem. Should I be trying to wrap the PandocMonad monad inside MonadIO so the function only a single typeclass constraint? I've tried removing the PandocMonad m constraint, which I had thought would have pushed the error down to applyFilters, where indeed an error about the lack of the PandocMonad m constraint does appear, but the original error remains as well.

like image 366
paid50-face-pretty Avatar asked Dec 14 '25 02:12

paid50-face-pretty


1 Answers

You here hint that your monad i a function, indeed the type is (let us ignore the context for now Pandoc -> m Pandoc.

But that only works if all the items in the do are thus of type a -> m b, and that is clearly not the case: getEngine is a type getEngine :: MonadIO m => m ScriptingEngine.

We thus can let do work over m with:

applyPandocFilters :: (MonadIO m, PandocMonad m) => Pandoc -> m Pandoc
applyPandocFilters document = do
  luaEngine <- getEngine
  applyFilters
    luaEngine
    Environment{envReaderOptions = readerOptions, envWriterOptions = writerOptions}
    [ LuaFilter "./filters/lua/filters.lua"
    , CiteprocFilter
    ]
    []
    document

now the do block has type m Pandoc, and thus luaEngine can be anything m a

Edit not by original commenter: Fixed provided code by adding missing argument to function

like image 108
Willem Van Onsem Avatar answered Dec 16 '25 02:12

Willem Van Onsem



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!