请考虑以下示例程序:
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 ()
它只能使用get而不是lift get因为instance MonadState s m => MonadState s (MaybeT m)在MaybeT模块中定义.
许多这样的实例是以组合爆炸方式定义的.
如果我们有以下类型类,那会很好(虽然不可能?为什么?)
{-# LANGUAGE MultiParamTypeClasses #-}
class SuperMonad m s where
  lifts :: m a -> s a
让我们尝试将其定义为:
{-# 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
使用lifts $ print i而不是liftIO $ print i工作,这很好.
但使用lifts (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)
我明白为什么" instance SuperMonad a a"适用.但为什么GHC认为另一个呢?
Nor*_*sey 35
跟进ephemient的优秀答案:Haskell类型类使用开放世界的假设:一些白痴可以在以后出现并添加一个不重复但与您的实例重叠的实例声明. 把它想象成一个对手游戏:如果对手可以使你的程序模棱两可,那么编译器就会黯然失色.
如果你正在使用GHC,你当然可以对编译器说"你的偏执狂地狱;请允许我使用我的模糊实例声明":
{-# LANGUAGE OverlappingInstances #-}
如果程序的后续演变导致你没想到的重载决议,那么编译器会得到1000个I-tell-you-so点:-)
仅仅因为您没有在当前模块中定义实例并不意味着无法在其他地方定义实例.
{-# 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
要么
instance (SuperMonad a b, MonadTrans t, Monad b) => SuperMonad a (t b)
  -- with a = StateT Int IO
  --      t = StateT Int
  --      b = IO
?
| 归档时间: | 
 | 
| 查看次数: | 4842 次 | 
| 最近记录: |