Haskell:重叠实例 [英] Haskell: Overlapping instances
问题描述
考虑下面的范例程序:
next :: Int - >诠释
下我
| 0 == m2 = d2
|否则= 3 * i + 1
其中
(d2,m2)= i`divMod` 2
loopIteration :: MaybeT(StateT Int IO)()
loopIteration = do
i< - 获得
guard $ i> 1
liftIO $ print i
修改下一个
main :: IO()
main = do
(`runStateT` 31)。 runMaybeT。永远$ loopIteration
return()
它只能使用 get
而不是 lift得到
,因为 instance MonadState sm =>在MaybeT模块中定义了MonadState(MaybeT m)
。
许多这样的实例是以组合爆炸方式定义的。 p>
如果我们有以下类型的类,它会很好(尽管不可能?为什么?):
<$ p $ { - #LANGUAGE MultiParamTypeClasses# - }
class SuperMonad ms where
lifts :: ma - > sa
让我们试着定义它:
{ - #LANGUAGE FlexibleInstances,...# - }
实例SuperMonad aa其中
升降机= id
实例(SuperMonad ab,MonadTrans t,Monad b)=> SuperMonad a(t b)
lift = lift。升降机
使用升降机$ print i
,而不是 liftIO $ print i
很好用。
但是使用 :StateT Int IO Int)
而不是(get :: MaybeT(StateT Int IO)Int)
不起作用。
GHC(6.10.3)给出以下错误:
SuperMonad $的重叠实例b $ b(StateT Int IO)(StateT Int IO)
由于使用'lifts'引起
匹配实例:
实例SuperMonad aa
实例(SuperMonad ab,MonadTrans t ,Monad b)=>
SuperMonad a(tb)
在'do'表达式中:
i < - lift(get :: StateT Int IO Int)
我可以看到为什么实例SuperMonad aa
适用。但为什么GHC认为另一个人也这样做?
为了跟进ephemient的优秀回答:Haskell类型类使用开放世界的假设:稍后有些白痴会出现,并添加一个不是重复的实例声明,但与您的实例重叠。 将其视为对手游戏:如果对手可能会让程序变得模糊不清,编译器就会瘫痪。
如果您使用GHC当然可以对编译器说:用你的偏执狂去地狱;让我模棱两可的实例声明:
{ - #LANGUAGE OverlappingInstances# - }
如果您的程序的后期演变导致超负荷解决方案,您没有想到,编译器可以获得1,000个I-Tell-You-So分数: - )
Consider the following example program:
next :: Int -> Int
next i
| 0 == m2 = d2
| otherwise = 3 * i + 1
where
(d2, m2) = i `divMod` 2
loopIteration :: MaybeT (StateT Int IO) ()
loopIteration = do
i <- get
guard $ i > 1
liftIO $ print i
modify next
main :: IO ()
main = do
(`runStateT` 31) . runMaybeT . forever $ loopIteration
return ()
It can only use get
instead of lift get
because instance MonadState s m => MonadState s (MaybeT m)
is defined in the MaybeT module.
Many such instances are defined in kind of a combinatoric explosion manner.
It would have been nice (although impossible? why?) if we had the following type-class:
{-# LANGUAGE MultiParamTypeClasses #-}
class SuperMonad m s where
lifts :: m a -> s a
Let's try to define it as such:
{-# LANGUAGE FlexibleInstances, ... #-}
instance SuperMonad a a where
lifts = id
instance (SuperMonad a b, MonadTrans t, Monad b) => SuperMonad a (t b) where
lifts = lift . lifts
Using lifts $ print i
instead of liftIO $ print i
works, which is nice.
But using lifts (get :: StateT Int IO Int)
instead of (get :: MaybeT (StateT Int IO) Int)
doesn't work.
GHC (6.10.3) gives the following error:
Overlapping instances for SuperMonad
(StateT Int IO) (StateT Int IO)
arising from a use of `lifts'
Matching instances:
instance SuperMonad a a
instance (SuperMonad a b, MonadTrans t, Monad b) =>
SuperMonad a (t b)
In a stmt of a 'do' expression:
i <- lifts (get :: StateT Int IO Int)
I can see why "instance SuperMonad a a
" applies. But why does GHC think that the other one does, too?
To follow up ephemient's excellent answer: Haskell type classes use an open-world assumption: some idiot can come along later and add an instance declaration that's not a duplicate and yet overlaps with your instance. Think of it as an adversary game: if an adversary can make your program ambiguous, the compiler bleats.
If you're using GHC you can of course say to the compiler "to hell with your paranoia; allow me my ambiguous instance declaration":
{-# LANGUAGE OverlappingInstances #-}
If later evolution of your program leads to overload resolution you didn't expect, the compiler gets 1,000 I-told-you-so points :-)
这篇关于Haskell:重叠实例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!