3

I'm writing the Monad instance for this type using ReaderT and deriving via

newtype HIO a = HIO {runHIO :: Set HiPermission -> IO a}

I've tried to do it this way

newtype HIO a = HIO {runHIO :: Set HiPermission -> IO a}
  deriving (Functor, Applicative, Monad) via (ReaderT (Set HiPermission) IO) 

but got error:

Couldn't match representation of type `Set HiPermission -> IO c' with that of `ReaderT (Set HiPermission) IO c' 
arising from the coercion of the method

What am I doing wrong?

Iceland_jack
  • 6,848
  • 7
  • 37
  • 46
Maria Z
  • 31
  • 2
  • 4
    `ReaderT` is both a type and a value constructor. For `DerivingVia` to work, the newtype constructor must be in scope, not only the type. That is, you need something like `import Control.Monad.Reader` or `import Control.Monad.Reader ( ReaderT(..) )` or `import Control.Monad.Reader ( ReaderT(ReaderT) )`. But `import Control.Monad.Reader ( ReaderT )` won't work, because it only imports the type. – danidiaz Jan 12 '22 at 12:32
  • The ghci command [`:instances ReaderT (Set HiPermission) IO`](https://ghc.gitlab.haskell.org/ghc/doc/users_guide/ghci.html#ghci-cmd-:instances) lists derivable instances, multi-parameter types are not listed for now so it doesn't list `MonadReader (Set HiPermission)` even though it can be derived. – Iceland_jack Jan 12 '22 at 14:52
  • 1
    @danidiaz I think you should make that an answer. @MariaZ presumably importing the `ReaderT` value-constructor does fix your problem? – leftaroundabout Jan 13 '22 at 12:27

1 Answers1

2

A likely cause of the problem is that you are importing the ReaderT type, but not the ReaderT value constructor, that is, the function of type (r -> m a) -> ReaderT r m a that builds values of type ReaderT.

For example, you might have written

import Control.Monad.Reader ( ReaderT ) -- only the ReaderT type

instead of

import Control.Monad.Reader -- import everything

or

import Control.Monad.Reader ( ReaderT(..) ) -- import the type and its constuctor

But, why is it necessary for the ReaderT value constructor to be in scope?

The reason is that DerivingVia relies on Data.Coerce under the hood. In the module which defines the instance, we must be able to coerce to the "via" type, in this case ReaderT (Set HiPermission) IO.

And, as it turns out, to be able to wrap/unwrap a newtype like ReaderT using coercions, we need to have its value constructor in scope. Quoting from the coerce Haddocks:

The third kind of instance exists for every newtype NT = MkNT T and comes in two variants, namely

instance Coercible a T => Coercible a NT

instance Coercible T b => Coercible NT b

This instance is only usable if the constructor MkNT is in scope.

danidiaz
  • 26,936
  • 4
  • 45
  • 95