Haskell:重叠实例

Haskell:重叠实例,haskell,typeclass,Haskell,Typeclass,考虑以下示例程序: 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 ma

考虑以下示例程序:

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 ()
让我们试着将其定义为:

{-# 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
使用
提升$print i
而不是
liftIO$print i
效果很好

但是使用
提升(get::StateT Int IO Int)
而不是
(get::MaybeT(StateT Int IO)Int)
不起作用

GHC(6.10.3)给出了以下错误:

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)
SuperMonad的重叠实例 (StateT Int IO)(StateT Int IO) 因使用“升降机”而引起的 匹配实例: 例如超声子a 实例(超声子a b,单子t,单子b)=> 超声子a(t b) 在“do”表达式的stmt中:
我仅仅因为您尚未在当前模块中定义实例,并不意味着无法在其他地方定义实例

{-# LANGUAGE ... #-}
module SomeOtherModule where

-- no practical implementation, but the instance could still be declared
instance SuperMonad (StateT s m) m
假设您的模块和
SomeOtherModule
在一个程序中链接在一起

现在,回答这个问题:您的代码是否使用

instance SuperMonad a a
  -- with a = StateT Int IO


继续ephemient的精彩回答:Haskell类型的类使用开放世界的假设:一些白痴可能稍后会出现,并添加一个不重复但与您的实例重叠的实例声明将其视为对手游戏:如果对手可以使您的程序模棱两可,编译器会发出嘶嘶声

如果您使用的是GHC,您当然可以对编译器说“让您的偏执狂见鬼去吧;请允许我使用模棱两可的实例声明”:


如果您的程序在以后的发展过程中产生了出乎您意料的过载解决方案,编译器将获得1000个I-teld-you-so点:-)

谢谢。但我仍然感到困惑:我的实例本身并不重叠,但有人可以定义一个会使我的实例重叠的实例。有人不能总是定义一个与我的实例重叠的实例吗?您的结构使得编译器无法明确地确定要在代码中使用哪个实例。如果可以明确地解决这个问题,那么其他地方的重叠就无关紧要了。@ephemient:那是因为编译器的工作方式,对吗?我这么说是因为我可以毫不含糊地确定可以使用哪个实例。不,这只是多参数TypeClass和特别灵活的实例所暴露的危险之一。重叠实例在我的扩展列表中排名靠后(甚至比不可判定的实例更进一步,这只会使编译器的工作更加困难)使用——不仅不可移植,而且还破坏了Haskell通常提供的安全保证。我建议OP接受它并手动处理
lift
ing或不
lift
ing,而不是添加此黑客……但这是我的意见。@ephemient:和你一样,我也感谢我的安全保证。我希望我的回答能让OP满意无懈可击清楚。。。
instance SuperMonad a a
  -- with a = StateT Int IO
instance (SuperMonad a b, MonadTrans t, Monad b) => SuperMonad a (t b)
  -- with a = StateT Int IO
  --      t = StateT Int
  --      b = IO
{-# LANGUAGE OverlappingInstances #-}