Say I have two type classes defined as follows that are identical in function but different in names:
class Monad m where
(>>=) :: m a -> (a -> m b) -> m b
return :: a -> m a
class PhantomMonad p where
pbind :: p a -> (a -> p b) -> p b
preturn :: a -> p a
Is there a way to tie these two classes together so something that is an instance of PhantomMonad will automatically be an instance of Monad, or will instances for each class have to be explicitly written? Any insight would be most appreciated, thanks!
Good answer: No, what you're hoping to do isn't really viable. You can write an instance that looks like it does what you want, possibly needing some GHC extensions in the process, but it won't work the way you you'd like it to.
Unwise answer: You can probably accomplish what you want using scary type-level metaprogramming, but it may get complicated. This really isn't recommended unless you absolutely need this to work for some reason.
Officially instances can't really depend on other instances, because GHC only looks at the "instance head" when making decisions, and class constraints are in the "context". To make something like a "type class synonym" here, you'd have to write what looks like an instance of Monad
for all possible types, which obviously doesn't make sense. You'll be overlapping with other instances of Monad
, which has its own problems.
On top of all that, I don't think such an instance will satisfy the termination check requirements for instance resolution, so you'd also need the UndecidableInstances
extension, which means the ability to write instances that will send GHC's type checker into an infinite loop.
If you really want to go down that rabbit hole, browse around on Oleg Kiselyov's website a bit; he's sort of the patron saint of type-level metaprogramming in Haskell.
It's fun stuff, to be sure, but if you just want to write code and have it work, probably not worth the pain.
Edit: Okay, in hindsight I've overstated the issue here. Something like PhantomMonad
works fine as a one-off and should do what you want, given the Overlapping
- and UndecidableInstances
GHC extensions. The complicated stuff starts up when you want to do anything much more complicated than what's in the question. My sincere thanks to Norman Ramsey for calling me on it--I really should have known better.
I still don't really recommend doing this sort of thing without good reason, but it's not as bad as I made it sound. Mea culpa.
That's an unusual design. Can you not just remove the PhantomMonad, since it is isomorphic to the other class.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With