Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to define a Monad instance "m a" with "a" in Typeclass Show?

Tags:

haskell

monads

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?

like image 940
frosch03 Avatar asked Jul 20 '11 15:07

frosch03


2 Answers

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.

like image 77
C. A. McCann Avatar answered Sep 19 '22 10:09

C. A. McCann


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.

like image 5
Daniel Wagner Avatar answered Sep 21 '22 10:09

Daniel Wagner