Open michaelt opened 8 years ago
A clearer statement of the difficulty: where we add a new MFunctor instance based on an old one, the actual implementation that uses the constrained t :: (* -> *) -> * -> *
will have to saturate t
with some f
or m
in order to use hoist on t m _
. But where I use hoist
in writing the instance I am almost certain to want to use fmap
. But (t f)
or (t m)
can't be mentioned in the constraint, since f
or m
can't ... since it's an MFunctor
instance we are defining.
I agree that something like RFunctor
is the correct solution. I'd be happy to add that class to mmorph
in some way, although maybe under another name. However, I don't think it needs to be a constraint on the MFunctor
class. Just make it a constraint on your instance:
instance (RFunctor t, MFunctor t) => MFunctor (Stream t)
Yes, the name was just to get it written down. I was only hooking it up with MFunctor
because that was the only place I needed it. Once I defined it it was nicer than the Monad m, Functor (t m) =>
in some other contexts.
Maybe the method could called
fmaps :: Monad m => (a -> b) -> t m a -> t m b
to express that fmaps
works the same for any monad or something like that. Then the class could be called, say Functors
with the mnemonic intimation that all the t m
s are functors, and that this is being expressed in one class. For the transformers
types, the trivial instance where fmaps = fmap
will always work; this is also the governing law of course.
I just tried these names here https://github.com/michaelt/trans/blob/master/Trans.hs I can't tell if the proximity to Functor
is just adding a layer of confusion.
The part I get stuck on here is what to call the class for this. I guess I could follow the Edward convention of Functor2
for naming higher-kinded equivalents of the same class and then use fmap2
as the class method.
It seems I very frequently run into trouble with
MFunctor
instances that carry anMFunctor
constraint forward. This is probably not a legitimate type, but consider:Writing a functor instance is simple
but if I set out to write
of course I get the complaint that
But the constraint can't be added, since
m
is not in scope. One can do an end run around this:Am I missing some simpler device? I have bumped to this more than once; maybe here I have a bad instance, but it will it think be a distraction to meditate on the particular case. It is fairly typical that you need a stronger fmap where you write
instance (MFunctor t) => MFunctor (... t ...)
since you can't presuppose(forall m. Monad m => Functor (t m))
It occurred to me that something like
RFunctor
(pardon the dumb name) could be a constraint onMFunctor
or the slightly strengthenedfmap
could be included in its methods. But I don't know.