开发者

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

开发者 https://www.devze.com 2023-03-22 04:33 出处:网络
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

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 Showable? It would be easier to just enforce the Show constraint at that point, then it'll naturally propagate where necessary.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号