15

次の場合:

> (liftM2 fromMaybe) (ioError $ userError "OOPS") (return $ Just "ok")

ghciは私に

*** Exception: user error (OOPS)

もちろん、fromMaybeは正しく機能しています。

> (liftM2 fromMaybe) (return $ "not me") (return $ Just "ok")
"ok"

しかし、IO操作が実行されてから破棄されているようです。

> (liftM2 fromMaybe) (putStrLn "computing.." >> "discarded") (return $ Just "ok")
computing..
"ok"

なぜこうなった?IOモナドを怠惰にする方法はありますか?

具体的にはvalue :: IO (Maybe a)、(クリーンで簡潔な)言い方を考えると

result <- (liftM2 fromMaybe) err value

結果を解凍するか、それに応じてIOErrorをスローしますか?

4

3 に答える 3

12

IOここでレイジーを作ることが正しい方向かどうかはわかりません。あなたがやりたいと思われることは、最初に に到達しMaybe、次にそれを排除することです。これはいくつかの方法で記述できますが、1 つのオプションを次に示します。

test :: IO (Maybe a) -> IO a
test = (>>= maybe (ioError $ userError "oops") return)
于 2011-12-16T23:02:40.090 に答える
10

から do 表記に変換するliftM2と、コードが失敗する理由は明らかです。

do x <- ioError $ userError "OOPS"
   y <- return $ Just "ok"
   return $ fromMaybe x y

無条件に例外をスローしているため、これは最初の行を超えることはありません。

Anthony の提案は問題なく機能しますが、スローされた特定の例外を気にしない場合は、パターン マッチングを使用することもできます。

do Just result <- value

パターンが一致しない場合は が呼び出さfailれ、IOモナドの場合は例外がスローされます。

> Just x <- return Nothing
*** Exception: user error (Pattern match failure in do expression at <interactive>:1:0-5)
于 2011-12-16T23:37:53.110 に答える
5

[the]結果をアンパックするか、それに応じてIOErrorをスローする(クリーンで簡潔な)方法は何ですか?

エラーのスローに頼らないことをお勧めします。代わりに、「エラー」を明示的に処理します。

maybeM :: Monad m => m b -> (a -> m b) -> m (Maybe a) -> m b
maybeM err f value = do
  x <- value
  case x of
    Just y  -> f y
    Nothing -> err

-- This can be written simply as:
maybeM err f value = do
  x <- value
  maybe err f x

-- or even shorter! This is starting to look like Anthony's answer :)
maybeM err f value = value >>= maybe err f

関数の入力と型は、それ自体で語るべきです。ケースに対して実行するアクションNothing、またはケース内の値に対して実行する関数を指定して使用しますJust。特定の入力の場合、これは次のようになります。

maybeM (ioError $ userError "OOPS") return (return $ Just "ok")

したがって、絶対に必要な場合、「結果をアンパックするか、IOError をスローする簡潔な方法」は次のようになります。

-- compare to fromJust, a function to be avoided
fromJustIO :: IO (Maybe a) -> IO a
fromJustIO = maybeM (ioError $ userError "OOPS") return

これの型シグネチャが実質的に であることに注意してくださいMaybe a -> a。これは の本質であり、危険信号を発するmagicMonadUnwrap :: Monad m => m a -> aはずです。ただし、この残虐行為を簡単な方法で使用できます。

result <- fromJustIO value

繰り返しになりますが、ここで例外を使用することは強くお勧めしません。maybeMエラーが発生した場合に実行する IO アクションを使用して提供することにより、単に爆発するよりも洗練された方法でエラーを処理してみてください。

于 2011-12-20T07:23:19.330 に答える