Lei*_*rts 0 haskell typeclass newtype overlapping-instances
我有一个类型类:
class Wrapper w where
open :: w -> Map String Int
close :: Map String Int -> w
Run Code Online (Sandbox Code Playgroud)
它看起来并不是很有用,但是我用它强烈地(不仅仅是一个type同义词)来区分语义上不同的Map String Ints:
newtype FlapMap = Flap (Map String Int)
newtype SnapMap = Snap (Map String Int)
...
Run Code Online (Sandbox Code Playgroud)
并且仍然具有可在任何类型的类上运行的函数.
Wrapper实例样板)?我想做这个:
instance (Wrapper wrapper) => Show wrapper where
show w = show $ toList $ open w
Run Code Online (Sandbox Code Playgroud)
而不是编写许多样板Show实例.
通过FlexibleInstances和UndecidableInstances,GHC引导我到一个点,它认为我的实例声明适用于所有内容,因为它据称与Show我的代码中的其他实例冲突GHC.Show.HaskellWiki和StackOverflow的回答者和HaskellWiki说服我OverlappingInstances不太安全,可能会让人困惑.GHC甚至没有建议.
为什么GHC首先抱怨不知道Show Int要选择哪个fx实例(为什么它不查看我在编译时给出的约束?)然后,被告知实例可能重叠,突然知道该怎么做?
我可以避免让OverlappingInstances我的newtypes?
如果没有OverlappingInstances,你可以做到这一点,正如你所提到的,这是不可预测的.无论如何,它对你没有帮助,所以如果没有包装类型,你几乎无法做到这一点.
当然,这是相当不满意的,为什么会这样呢?正如您已经确定的那样,GHC在选择实例时不会查看实例上下文,只会查看实例头.为什么?好吧,请考虑以下代码:
class Foo a where
fooToString :: a -> String
class Bar a where
barToString :: a -> String
data Something = Something
instance Foo Something where
fooToString _ = "foo something"
instance Bar Something where
barToString _ = "bar something"
instance Foo a => Show a where
show = fooToString
instance Bar a => Show a where
show = barToString
Run Code Online (Sandbox Code Playgroud)
如果您单独考虑Foo或Bar类型类,则上述定义是有意义的.任何实现Foo类型类的东西都应该Show"免费" 获得一个实例.不幸的是,Bar实例也是如此,所以现在你有两个有效的实例show Something.
由于类型类总是打开的(事实上,Show如果你能够为它定义自己的实例,它必须是开放的),不可能知道某人不会出现并添加他们自己的类似实例,然后在你的数据类型上创建一个实例,造成歧义.这实际上是来自类型形式的OO多重继承的经典钻石问题.
您可以获得的最好的方法是创建一个提供相关实例的包装器类型:
{-# LANGUAGE ExistentialQuantification #-}
data ShowableWrapper = forall w. Wrapper w => ShowableWrapper w
instance Show ShowableWrapper where
show (ShowableWrapper w) = show . toList $ open w
Run Code Online (Sandbox Code Playgroud)
不过,在那时,你真的没有比仅仅编写自己的showWrapper :: Wrapper w => w -> String功能更有优势了.