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.
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