tl; dr upfront:seq
が唯一の方法です。
の実装はIO
規格で規定されていないため、特定の実装のみを見ることができます。ソースから入手できるGHCの実装を見ると(舞台裏での特別な扱いの一部がIO
モナド法の違反を引き起こしている可能性がありますが、私はそのような出来事を認識していません)、
-- in GHC.Types (ghc-prim)
newtype IO a = IO (State# RealWorld -> (# State# RealWorld, a #))
-- in GHC.Base (base)
instance Monad IO where
{-# INLINE return #-}
{-# INLINE (>>) #-}
{-# INLINE (>>=) #-}
m >> k = m >>= \ _ -> k
return = returnIO
(>>=) = bindIO
fail s = failIO s
returnIO :: a -> IO a
returnIO x = IO $ \ s -> (# s, x #)
bindIO :: IO a -> (a -> IO b) -> IO b
bindIO (IO m) k = IO $ \ s -> case m s of (# new_s, a #) -> unIO (k a) new_s
thenIO :: IO a -> IO b -> IO b
thenIO (IO m) k = IO $ \ s -> case m s of (# new_s, _ #) -> unIO k new_s
unIO :: IO a -> (State# RealWorld -> (# State# RealWorld, a #))
unIO (IO a) = a
(厳密な)状態モナドとして実装されます。したがって、モナド法の違反は、IO
によっても行われControl.Monad.State[.Strict]
ます。
モナドの法則を見て、何が起こるか見てみましょうIO
:
return x >>= f ≡ f x:
return x >>= f = IO $ \s -> case (\t -> (# t, x #)) s of
(# new_s, a #) -> unIO (f a) new_s
= IO $ \s -> case (# s, x #) of
(# new_s, a #) -> unIO (f a) new_s
= IO $ \s -> unIO (f x) s
newtypeラッパーを無視すると、はreturn x >>= f
になり\s -> (f x) s
ます。それを(おそらく)区別する唯一の方法f x
はですseq
。(そして、seq
それを区別できるのはf x ≡ undefined
。)
m >>= return ≡ m:
(IO k) >>= return = IO $ \s -> case k s of
(# new_s, a #) -> unIO (return a) new_s
= IO $ \s -> case k s of
(# new_s, a #) -> (\t -> (# t, a #)) new_s
= IO $ \s -> case k s of
(# new_s, a #) -> (# new_s, a #)
= IO $ \s -> k s
newtypeラッパーを再び無視すると、k
に置き換えられます。これも、。\s -> k s
によってのみ区別seq
できk ≡ undefined
ます。
m >>= (\x -> g x >>= h) ≡ (m >>= g) >>= h:
(IO k) >>= (\x -> g x >>= h) = IO $ \s -> case k s of
(# new_s, a #) -> unIO ((\x -> g x >>= h) a) new_s
= IO $ \s -> case k s of
(# new_s, a #) -> unIO (g a >>= h) new_s
= IO $ \s -> case k s of
(# new_s, a #) -> (\t -> case unIO (g a) t of
(# new_t, b #) -> unIO (h b) new_t) new_s
= IO $ \s -> case k s of
(# new_s, a #) -> case unIO (g a) new_s of
(# new_t, b #) -> unIO (h b) new_t
((IO k) >>= g) >>= h = IO $ \s -> case (\t -> case k t of
(# new_s, a #) -> unIO (g a) new_s) s of
(# new_t, b #) -> unIO (h b) new_t
= IO $ \s -> case (case k s of
(# new_s, a #) -> unIO (g a) new_s) of
(# new_t, b #) -> unIO (h b) new_t
今、私たちは一般的に持っています
case (case e of case e of
pat1 -> ex1) of ≡ pat1 -> case ex1 of
pat2 -> ex2 pat2 -> ex2
言語レポートの式3.17.3。(a)に従って、この法則はモジュロだけではありませんseq
。
要約すると、とを区別できるIO
という事実を除いて、モナドの法則を満たします。同じことが、、、、および関数型をラップする他のモナドにも当てはまります。seq
undefined
\s -> undefined s
State[T]
Reader[T]
(->) a