How to define a Monad instance "m a" with "a" in Typeclass Show?
I would like to define a monad instance with the container M as monad and with the contained type a
which should be a member of class Show
. This constraint (that a
is member of Show
) should be ensured by the type system.
I gave it a try like this, but M
is unfortunately not of the right Kind:
data M = forall a. Show a => M a
instance Monad M where
return x = M x
All other attempts to achieve that, run into the following problem:开发者_运维技巧 Since Monad
is a constructor class, I don't have explicit access to the type a
of the contained element(s), so I can't restrict it.
Does anyone know a solution to this without defining a new Monad
class?
Well, it is actually possible to restrict the parameters of a type constructor in some sense, using GADTs:
data M a where
M :: (Show a) => a -> M a
Unfortunately this doesn't actually help you here. In a way it actually makes things worse, because rather than having a Monad
instance without the constraint, it becomes impossible to write the instance at all.
If you look at the above constructor type signature, it clearly resembles return
--which demonstrates why what you're doing is fundamentally impossible. The type for return is: (Monad m) => a -> m a
, and as always unbound type variables are implicitly universally quantified at the outermost level, so you can read that as "for all possible types a
, and all possible types m
which are instances of Monad
, given a value of type a
you can construct a value of type m a
". The "for all" phrasing is quite literal there--the type of return isn't just using a type variable, it's actively asserting that any type a
whatsoever must be allowed.
So, in short, no. There's no way to do what you want because the standard Monad
class explicitly specifies the opposite.
You may not be able to do exactly what you're asking, but another possibility is for your particular monad to provide an action that explicitly does whatever you're thinking of doing with Show
. That is, supposing you have:
data M a = {- ... -}
instance Monad M where -- notice: no Show constraint
{- ... -}
Then you could additionally supply some action:
report :: Show a => M a -> M a
I can't think off the top of my head of a good use of this pattern with Show
, but I do know of a similar example where you might wish for an Ord
constraint. The setup is that you'd like to make a monad that's nondeterministic (like [a]
), but doesn't have duplicates (like Set a
). Removing duplicates requires some context like Eq
or Ord
, but we can't demand that on every return
/>>=
operation. So instead we demand that the user explicitly mark the points where duplicates should be coalesced:
newtype Setlike a = Setlike { toList :: [a] }
instance Monad Setlike where
return x = Setlike [x]
Setlike xs >>= f = [y | x <- xs, let Setlike ys = f x, y <- ys]
collapse :: Ord a => Setlike a -> Setlike a
collapse = Setlike . Data.Set.toList . Data.Set.fromList . toList
This can be used like so:
valuesOfInterest = collapse $ do
v1 <- allValues
v2 <- allValues
doSomethingInteresting v1 v2
Then, even if some pairing of v1
and v2
happen to result in the same value of interest, that value will only appear once in the result.
Some similar trick is likely possible for your use case as well.
No. This is not possible, although it is easy to explain. Have a look at the type signature of return
:
return :: Monad m => a -> m a
The signature can't change, so your monad must accept any content types. Since the type class itself does not mention the content type, there is no way to enforce constraints on it.
One thing you could do is writing this constraint on all functions that need it and dropping the global restriction. The soundness of the system is still guaranteed, if you can can proof the existance of a Show
constraint only when it is needed.
This is possible with some extreme trickery. See the rmonad package for an implementation. However, it's probably not worth it.
Why do you need to have the constraint that a
is Show
able? It would be easier to just enforce the Show constraint at that point, then it'll naturally propagate where necessary.
精彩评论