次のサンプルプログラムについて考えてみます。
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 ()
MaybeTモジュールで定義されているため、get
代わりにのみ使用できます。lift get
instance MonadState s m => MonadState s (MaybeT m)
そのようなインスタンスの多くは、一種の組み合わせ爆発の方法で定義されます。
次の型クラスがあれば、それは良かったでしょう(不可能ではありますか?なぜですか?)。
{-# 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は、もう一方もそうだと考えているのでしょうか。