简体   繁体   English

如何在 Typeclass Show 中用“a”定义 Monad 实例“ma”?

[英]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 .我想用容器 M 定义一个 monad 实例作为 monad 并且包含的类型a应该是 class Show的成员。 This constraint (that a is member of Show ) should be ensured by the type system.类型系统应确保此约束(即aShow的成员)。

I gave it a try like this, but M is unfortunately not of the right Kind:我像这样尝试过,但不幸的是M不是正确的类型:

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.实现这一目标的所有其他尝试都会遇到以下问题:由于Monad是构造函数 class,我没有显式访问所包含元素的a类型,因此我无法限制它。

Does anyone know a solution to this without defining a new Monad class?有没有人知道这个没有定义新的Monad class 的解决方案?

Well, it is actually possible to restrict the parameters of a type constructor in some sense, using GADTs:嗯,实际上可以在某种意义上限制类型构造函数的参数,使用 GADT:

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.在某种程度上,它实际上使事情变得更糟,因为没有约束的Monad实例,根本不可能编写实例。

If you look at the above constructor type signature, it clearly resembles return --which demonstrates why what you're doing is fundamentally impossible.如果您查看上面的构造函数类型签名,它显然类似于return ——这说明了为什么您正在做的事情根本上是不可能的。 The type for return is: (Monad m) => a -> ma , 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 ma ".返回的类型是: (Monad m) => a -> ma ,并且像往常一样,未绑定的类型变量在最外层被隐式地普遍量化,因此您可以将其读作“对于所有可能的类型a和所有可能的类型m这是Monad的实例,给定 a 类型a值,您可以构造ma类型的值。 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.这里的“for all”措辞非常直白—— return 的类型不仅仅是使用类型变量,它还积极地断言必须允许任何类型a

So, in short, no.所以,简而言之,没有。 There's no way to do what you want because the standard Monad class explicitly specifies the opposite.没有办法做你想做的事,因为标准Monad class明确指定了相反的内容。

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 .您可能无法完全按照您的要求进行操作,但另一种可能性是您的特定 monad 提供一个动作,该动作明确地执行您想要使用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.我想不出用Show很好地使用这种模式,但我知道一个类似的例子,你可能希望使用Ord约束。 The setup is that you'd like to make a monad that's nondeterministic (like [a] ), but doesn't have duplicates (like Set a ).设置是您想要创建一个不确定的 monad(如[a] ),但没有重复项(如Set a )。 Removing duplicates requires some context like Eq or Ord , but we can't demand that on every return / >>= operation.删除重复项需要一些上下文,例如EqOrd ,但我们不能在每次return / >>=操作时都要求这样做。 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.然后,即使v1v2的某些配对碰巧产生相同的感兴趣值,该值也只会在结果中出现一次。

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的类型签名:

return :: Monad m => a -> m a

The signature can't change, so your monad must accept any content types.签名不能改变,所以你的 monad 必须接受任何内容类型。 Since the type class itself does not mention the content type, there is no way to enforce constraints on it.由于类型 class 本身没有提及内容类型,因此无法对其强制执行约束。

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.如果您可以仅在需要时证明Show约束的存在,系统的健全性仍然得到保证。

This is possible with some extreme trickery.这可以通过一些极端的技巧来实现。 See the rmonad package for an implementation.有关实现,请参见rmonad package。 However, it's probably not worth it.但是,这可能不值得。

Why do you need to have the constraint that a is Show able?为什么你需要有a is Show的约束? It would be easier to just enforce the Show constraint at that point, then it'll naturally propagate where necessary.在那个时候强制执行 Show 约束会更容易,然后它会在必要时自然传播。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM