質問
次のプログラム例を考えてみましょう。
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は相手も同様だと考えるのでしょうか?
解決
ephemient の優れた回答をフォローアップするには、次のようにします。Haskell 型クラスは オープンワールドの想定:バカが後からやって来て、次のようなインスタンス宣言を追加する可能性があります。 重複ではありません そしてまだ と重なる あなたのインスタンス。 敵対ゲームとして考えてください:攻撃者がプログラムを曖昧にする可能性がある場合、コンパイラは動作を停止します。
GHC を使用している場合は、もちろんコンパイラーに「あなたの偏執症は地獄だ」と言うことができます。あいまいなインスタンス宣言を許可してください":
{-# LANGUAGE OverlappingInstances #-}
その後のプログラムの進化により、予想外のオーバーロードが解決された場合、コンパイラーは 1,000 ポイントを獲得します :-)
他のヒント
ちょうどあなたがあなたの現在のモジュールでインスタンスを定義していないので、1つは、どこか別の場所に定義することができなかったことを意味するものではありません。
{-# 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