Binding / Combining Type Classes in Haskell

Let's say I have two types of classes, defined as follows, which are identical in function but differ in name:

class Monad m where (>>=) :: ma -> (a -> mb) -> mb return :: a -> ma class PhantomMonad p where pbind :: pa -> (a -> pb) -> pb preturn :: a -> pa 

Is there a way to link these two classes together, so something that is an instance of PhantomMonad will automatically be an instance of Monad, or should the instances for each class be explicitly written? Any insight would be greatly appreciated, thanks!

+9
haskell monads typeclass
May 20 '10 at 19:52
source share
5 answers

Good answer: No, what you hope to do is not really viable. You can write an instance that looks like it is doing what you want, it might have some GHC extensions, but it will not work the way you would like.

Unreasonable answer: Perhaps you can accomplish what you want using frightening metaprogramming at the type level, but this can get complicated. It really is not recommended unless you need this job for some reason.

Officially, instances cannot really depend on other instances, because the GHC only looks at the "instance head" when making decisions, and the class constraints are in the "context". To do something like a โ€œclass type synonymโ€ here, you need to write something that looks like an instance of Monad for all possible types, which obviously doesn't make sense. You will overlap with other Monad instances that have their problems.

Among other things, I donโ€™t think that such an instance will satisfy the completion check requirements to resolve the instance, so you will also need the UndecidableInstances extension, which means the ability to write instances that will send the GHC type checker to an infinite loop.

If you really want to go down this rabbit hole, check out the site a bit

+13
May 20 '10 at 8:16 p.m.
source share

Is there a way to link these two classes together, so something that is an instance of PhantomMonad will automatically become an instance of Monad?

Yes, but this requires slightly disturbing extensions to the FlexibleInstances and UndecidableInstances languages:

 instance (PhantomMonad m) => Monad m where return = preturn (>>=) = pbind 

FlexibleInstances not so bad, but the risk of insolubility is somewhat more alarming. The problem is that nothing in the output rule gets smaller, so if you combine this instance declaration with another similar one (for example, vice versa), you can easily get type checking for a loop forever.

I usually find it convenient to use FlexibleInstances , but I try to avoid UndecidableInstances no good reason. Here, I agree with Don Stewart's suggestion that you better use Monad to get started. But your question is more like the nature of a thought experiment, the answer is that you can do what you want without falling into Olegโ€™s level of boredom.

+7
May 21 '10 at 2:26 a.m.
source share

This is an unusual design. Can you not just remove PhantomMonad, as it is isomorphic to another class.

+6
May 20 '10 at 20:06
source share

Another solution is to use newtype . This is not exactly what you want, but is often used in such cases.

This allows you to link different ways of specifying the same structure. For example, ArrowApply (from Control.Arrow) and Monad equivalent. You can use Kleisli to make ArrowApply from a monad, and ArrowMonad to make a monad from ArrowApply.

In addition, one-sided wrappers are possible: WrapMonad (in Control.Applicative) forms an applicative exit from the monad.

 class PhantomMonad p where pbind :: pa -> (a -> pb) -> pb preturn :: a -> pa newtype WrapPhantom ma = WrapPhantom { unWrapPhantom :: ma } newtype WrapReal ma = WrapReal { unWrapReal :: ma } instance Monad m => PhantomMonad (WrapPhantom m) where pbind (WrapPhantom x) f = WrapPhantom (x >>= (unWrapPhantom . f)) preturn = WrapPhantom . return instance PhantomMonad m => Monad (WrapReal m) where WrapReal x >>= f = WrapReal (x `pbind` (unWrapReal . f)) return = WrapReal . preturn 
+3
May 21 '10 at 9:01 a.m.
source share

Although that doesn't make sense, try

 instance Monad m => PhantomMonad m where pbind = (>>=) preturn = return 

(possibly with some compiler warnings disabled).

+1
May 20 '10 at 20:09
source share



All Articles