the*_*ian 10 monads haskell typeclass
假设我有两个类型定义如下,功能相同但名称不同:
class Monad m where
(>>=) :: m a -> (a -> m b) -> m b
return :: a -> m a
class PhantomMonad p where
pbind :: p a -> (a -> p b) -> p b
preturn :: a -> p a
Run Code Online (Sandbox Code Playgroud)
有没有办法将这两个类绑定在一起,因此作为PhantomMonad实例的东西将自动成为Monad的实例,或者每个类的实例是否必须明确写入?任何见解都将非常感谢,谢谢!
C. *_*ann 13
很好的答案:不,你希望做的事情并不可行.你可以编写一个看起来像你想要的实例,可能在这个过程中需要一些GHC扩展,但它不会按照你喜欢的方式工作.
不明智的回答:你可以使用可怕的类型级元编程来完成你想要的东西,但它可能会变得复杂.除非您出于某种原因绝对需要这项工作,否则不建议这样做.
正式实例不能真正依赖于其他实例,因为GHC只在决策时查看"实例头",而类约束在"上下文"中.为了让像一个"类型类同义词"在这里,你必须写什么样子的一个实例Monad为所有可能的类型,这显然是没有意义的.您将与其他Monad具有自身问题的实例重叠.
最重要的是,我认为这样的实例不会满足实例解析的终止检查要求,因此您还需要UndecidableInstances扩展,这意味着能够编写将GHC的类型检查器发送到无限循环的实例.
如果你真的想要去那个兔子洞,请浏览一下Oleg Kiselyov的网站 ; 他是Haskell中类型级元编程的守护神.
这很有趣,但是如果你只是想编写代码并让它工作,那可能不值得痛苦.
编辑:好的,事后我在这里夸大了问题.PhantomMonad考虑到Overlapping- 和UndecidableInstancesGHC扩展,类似的东西可以作为一次性工作,并且应该做你想要的.当你想做任何比问题更复杂的事情时,复杂的东西就会启动.真诚地感谢诺曼拉姆齐给我打电话 - 我真的应该知道的更好.
我仍然没有真正推荐没有充分理由做这种事情,但它并没有我听起来那么糟糕.Mea culpa.
这是一个不寻常的设计.你能不能只删除PhantomMonad,因为它与其他类是同构的.
有没有办法把这两个类绑在一起,所以作为PhantomMonad实例的东西会自动成为Monad的一个实例?
是的,但它需要稍微令人担忧的语言扩展FlexibleInstances和UndecidableInstances:
instance (PhantomMonad m) => Monad m where
return = preturn
(>>=) = pbind
Run Code Online (Sandbox Code Playgroud)
FlexibleInstances并不是那么糟糕,但不可判断的风险稍微令人担忧.问题是在推理规则中,没有什么东西变小,所以如果你将这个实例声明和另一个类似的声明组合起来(比如反方向),你可以很容易地让类型检查器永远循环.
我一般都习惯使用FlexibleInstances,但我倾向于避免UndecidableInstances没有很好的理由.在这里,我同意Don Stewart的建议,即你最好先使用Monad它.但是你的问题更多的是思想实验的本质,答案是你可以做你想做的事情,而不会陷入奥列格的恐惧程度.