3

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.

2 Answers 2

3

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

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you. If I understand your answer correctly, the issue is that original code essentially wraps the entire Pandoc -> m Pandoc function inside a second monad? Unfortunately, I've tried your solution, and it still fails to compile: haskell • Couldn't match type ‘m’ with ‘(->) Pandoc’ Expected: m Pandoc Actual: Pandoc -> m0 Pandoc ‘m’ is a rigid type variable bound by the type signature for: applyPandocFilters :: forall (m :: * -> *). (MonadIO m, PandocMonad m) => Pandoc -> m Pandoc
I realized the issue, the provided code had been leaving off the argument [] after the list of filters. It now compiles. Thank you very much!
1

You need a type m which is both an instance of PandocMonad and also an instance of MonadIO. If you go to the documentation for a type class, you can see its instances. Note: the instances of a type class will generally be types (not type classes).

In this case, we go to the documentation for the PandocMonad type class and scroll down to the list of instances. Among other instances, we see that one of the types has the very appropriate-sounding name PandocIO. If we click on that and we look at its instances we can see that it has both an instance for MonadIO as well as the original PandocMonad instance that allowed us to find it.

So, one type we can put for m that fits those constraints would be the PandocIO type.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.