2

我想编写一个函数,它使用getLine并返回一个元组(整数,整数)但使用do-notation读取一些数据。像这样的东西(当然它不起作用):

fun :: (Integer, Integer)
fun = do
    a <- read (getLine::Integer)
    b <- read (getLine::Integer)
    return (a, b)

我必须为此编写自己的 monad 吗?有什么不写新单子的解决方案吗?


编辑

所以我可以编写main使用的函数fun,我认为这是唯一的解决方案:

main :: IO ()
main = do
    tuple <- fun
    putStrLn (show tuple)

fun :: IO (Integer, Integer)
fun = do
    a1 <- getLine
    b1 <- getLine
    let a = read (a1)
        b = read (b1)
    return (a, b)

以上代码有效。

4

2 回答 2

1

你的功能类型应该是

fun :: IO (Integer, Integer)

正如@kaan 所提到的,您不应该尝试从 monad 中获取 mondic 值(有副作用),因为这会破坏引用透明度。fun无论运行多少次,运行都应该始终返回相同的值,如果我们使用您的类型,这将不会发生。但是,如果类型是IO (Integer, Integer),则每次使用该函数时它都会返回相同的操作,并且运行此操作实际上会执行从控制台读取值的副作用。

回来使用你的功能。你可以在另一个IO单子中做到这一点,比如

main = do
  (a,b) <- fun
  print a
  print b

尽管有一些方法可以避免IO使用不安全的函数,但在您确切知道自己在做什么之前不建议这样做。

于 2013-06-18T19:08:06.553 回答
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
于 2013-06-18T20:12:56.880 回答