1

関数を 2 回適用し、結果をタプルにバインドできます。


  let foo :: Num a => a -> a
      foo x = x + 1

  let (x,y) = (foo 10, foo 20)

doこれは、ブロック内では実行できません (少なくとも、適切に実行する方法がわかりません) 。


  let bar :: Num a => a -> IO a
      bar x = do
                  let y = x + 1
                  return y

  let test :: Num a => IO a
      test = do
                 (x,y) <- (bar 10, bar 20)
                 return y

GHCI REPL と入力すると、次のエラーが発生しました。


:29:15:
    Couldn't match expected type ‘IO a1’ with actual type ‘(t0, a)’
    Relevant bindings include
      test :: IO a (bound at :28:5)
    In the pattern: (x, y)
    In a stmt of a 'do' block: (x, y) <- (bar 10, bar 20)
    In the expression:
      do { (x, y) <- (bar 10, bar 20);
           return y }

:29:24:
    Couldn't match type ‘(,) (IO a0)’ with ‘IO’
    Expected type: IO (IO a1)
      Actual type: (IO a0, IO a1)
    In a stmt of a 'do' block: (x, y) <- (bar 10, bar 20)
    In the expression:
      do { (x, y) <- (bar 10, bar 20);
           return y }
    In an equation for ‘test’:
        test
          = do { (x, y) <- (bar 10, bar 20);
                 return y }

私は明らかにもっと冗長でそれを解決できます:


  let test' :: Num a => IO a
      test' = do
                 x <- bar 10
                 y <- bar 20
                 return y

testのようにせずに表現する正しい方法はありtest'ますか?

4

3 に答える 3

7
import Control.Applicative

test = do (x,y) <- (,) <$> bar 10 <*> bar 20
          return y

別名(x,y) <- liftA2(,) (bar 10) (bar 20)

もちろん、この特定の例 ( がx捨てられているところ) については、同等であり、単に次のように記述する方がはるかに優れています。

test = bar 20
于 2015-09-18T08:28:30.083 に答える
6

私はあなたのコードにいくつかの変更を提案する自由を取ります. これが私のバージョンです:

import Control.Monad

-- no need for the do and let
bar :: Num a => a -> IO a
bar x = return $ x + 1 -- or: bar = return . (1+)

-- liftM2 to make (,) work on IO values
test :: Num a => IO a
test = do (x,y) <- liftM2 (,) (bar 10) (bar 20) -- or: (,) <$> bar 10 <*> bar 20
          return y

-- show that this actually works
main :: IO ()
main = test >>= print

タイプが一致しませんでした: タイプに(bar 10, bar 20)評価されNum a => (IO a, IO a)ますが、 として扱われNum a => IO (a, a)ます。持ち上げる(,)ことで、値に対して機能させ、IO値を返しIOます。

これを見てください (GHCiimport Control.Monadを取得するにはliftM2):

:t (,)
-- type is :: a -> b -> (a, b)

:t liftM2 (,)
-- type is :: Monad m => m a -> m b -> m (a, b)

私たちの場合MonadIOモナドです。したがって、 の最終出力は、適切な値を返すためliftM2 (,)、do ブロック内で適切に機能します。IOIO

そして、もちろん、この特定の問題は、より簡潔な方法で解決できます。

test'' = bar 20

IOPS:理由もなくモナドに何かを返さないでください。完全に純粋な操作を純粋でないように見せており、元に戻す合理的な方法はありません。

于 2015-09-18T08:29:39.073 に答える