将函数应用程序的结果存储在 DO 块内的元组中
Store result of function applications in a tuple inside a DO-block
虽然我可以应用一个函数两次并将结果绑定到一个元组中:
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'
?
你需要一个辅助函数来从元组内部提取 IO
为了简洁起见,我将使用 Int
而不是 Num a => a
(bar 1, bar 2) :: (IO Int, IO Int)
因此你需要有签名的东西
liftTuple :: (IO x, IO y) -> IO (x, y)
liftTuple (mx, my) = ...
然后你可以做 (x,y) <- liftTuple (bar 1, bar 2)
import Control.Applicative
test = do (x,y) <- (,) <$> bar 10 <*> bar 20
return y
又名(x,y) <- liftA2(,) (bar 10) (bar 20)
。
当然,对于这个具体的例子(x
只是被扔掉了),只写
是等价的并且更好
test = bar 20
我会自由地建议对您的代码进行一些更改。这是我的版本:
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
值,并且 returning 一个 IO
值。
看看这个(GHCi,import Control.Monad
得到liftM2
):
:t (,)
-- type is :: a -> b -> (a, b)
:t liftM2 (,)
-- type is :: Monad m => m a -> m b -> m (a, b)
在我们的例子中,Monad
是 IO
monad。因此,liftM2 (,)
的最终输出将在 IO
do 块中很好地工作,因为它 return 是一个合适的 IO
值。
而且,当然你可以用不那么冗长的方式解决这个特殊问题:
test'' = bar 20
PS:请不要 return 无缘无故地填充到 IO
monad 中。您正在让完美的纯操作看起来不纯,并且没有合理的回头路。
虽然我可以应用一个函数两次并将结果绑定到一个元组中:
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'
?
你需要一个辅助函数来从元组内部提取 IO
为了简洁起见,我将使用 Int
而不是 Num a => a
(bar 1, bar 2) :: (IO Int, IO Int)
因此你需要有签名的东西
liftTuple :: (IO x, IO y) -> IO (x, y)
liftTuple (mx, my) = ...
然后你可以做 (x,y) <- liftTuple (bar 1, bar 2)
import Control.Applicative
test = do (x,y) <- (,) <$> bar 10 <*> bar 20
return y
又名(x,y) <- liftA2(,) (bar 10) (bar 20)
。
当然,对于这个具体的例子(x
只是被扔掉了),只写
test = bar 20
我会自由地建议对您的代码进行一些更改。这是我的版本:
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
值,并且 returning 一个 IO
值。
看看这个(GHCi,import Control.Monad
得到liftM2
):
:t (,)
-- type is :: a -> b -> (a, b)
:t liftM2 (,)
-- type is :: Monad m => m a -> m b -> m (a, b)
在我们的例子中,Monad
是 IO
monad。因此,liftM2 (,)
的最终输出将在 IO
do 块中很好地工作,因为它 return 是一个合适的 IO
值。
而且,当然你可以用不那么冗长的方式解决这个特殊问题:
test'' = bar 20
PS:请不要 return 无缘无故地填充到 IO
monad 中。您正在让完美的纯操作看起来不纯,并且没有合理的回头路。