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