我想编写一个函数,使用getLine
读取一些数据并返回一个元组(整数,整数)但使用do-notation。这样的事情(当然不起作用):
fun :: (Integer, Integer)
fun = do
a <- read (getLine::Integer)
b <- read (getLine::Integer)
return (a, b)
我必须为此写自己的monad吗?有没有解决方案不写新的monad?
修改
所以我可以编写使用main
的{{1}}函数,我认为这是唯一的解决方案:
fun
以上代码可以使用。
答案 0 :(得分:1)
你的功能类型应该是
fun :: IO (Integer, Integer)
正如@kaan所提到的,你不应该试图从monad中获得mondic值(带有副作用),因为这会破坏参照透明度。运行fun
应始终返回相同的值,无论它运行多少次,如果我们使用您的类型,这将不会发生。但是,如果类型为IO (Integer, Integer)
,则每次使用该函数时它都会返回相同的操作,并且运行此操作实际上会执行从控制台读取值的副作用。
回到使用你的功能。您可以在其他IO
monad中执行此操作,例如
main = do
(a,b) <- fun
print a
print b
虽然有些方法可以使用不安全的功能从IO
中解决问题,但在您确切知道自己在做什么之前不建议这样做。
答案 1 :(得分:1)
如上所述,您需要fun
类型IO (Integer, Integer)
而不是(Integer, Integer)
。然而,一旦你已经让自己屈服于这种命运,有很多方法可以让这只猫变老。以下是一些让你的想象力发挥作用的方法。
fun = do
a <- getLine
b <- getLine
return (read a, read b)
-- import Control.Applicative for (<$>)
-- can also spell (<$>) as fmap, liftA, liftM, and others
fun = do
a <- read <$> getLine
b <- read <$> getLine
return (a, b)
fun = do
a <- readLn
b <- readLn
return (a, b)
fun = liftM2 (,) readLn readLn
-- different type!
-- use in main like this:
-- main = do
-- [a, b] <- fun
-- foo
-- import Control.Monad for replicateM
fun :: IO [Integer]
fun = replicateM 2 readLn