Haskell:重叠实例 [英] Haskell: Overlapping instances

查看:149
本文介绍了Haskell:重叠实例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑下面的范例程序:

  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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆