Haskell: 如何在 do 块下使用 where 语句

Haskell: How to use a where statement under a do block

我正在用 Haskell 编写一个程序,它是 IO,当我遇到一个我不明白的错误时。当我在 do 块后使用 where 语句时,它不会做与没有 do 块相同的事情。

有效的程序是:

import Control.Monad
prog :: IO()
prog = do m <- getLine
          n <- getLine
          p <- getLine
          replicateM_ (read m :: Int) (putStrLn n)
          replicateM_ (read m :: Int) (putStrLn p)

但是当我用这样的 where 语句替换 read m :: Int 时:

import Control.Monad
prog1 :: IO()
prog1 = do m <- getLine
           n <- getLine
           p <- getLine
           replicateM_ (a) (putStrLn n)
           replicateM_ (a) (putStrLn p)
           where
           a = read m :: Int 

我收到错误:

Template.hs:23:21: error: Variable not in scope: m :: String
   |
23 |            a = read m :: Int 
   |                     ^

我查看了可能是什么问题,我认为这与 m 的类型有关,即 IO String。我知道你必须留在 IO 类型中(一旦你在其中)才能使用字符串。但是我不明白为什么 'where' 会“爆发”这种IO类型。据我了解,我给出的两个示例在功能上是相同的。一开始我以为写程序没有where是解决不了的,因为函数读取的是类型read :: Read a => String -> a,而我在第一个程序中的输入也是IO String。那么为什么我的第一个程序没有报错呢?有人可以解释我理解错了什么以及如何修复我的程序以便我只需要执行一次 read m :: Int 吗?关于如何在 do 块下使用 where 语句的一些提示也会有所帮助。

我遇到问题的原始程序更长,而且并不完全相关,所以我使用这个最小的工作示例来解释我的问题的本质。在我原来的程序中,我在 where 之后有多个语句,所以我不想像我在这个例子中那样全部替换它。

do 块中的绑定对其后的 where 语句是不透明的,因此您不能在 [=12= 内引用 do 块中定义的任何内容] 陈述。您也不需要,因为您可以直接在 do:

中使用 let
prog1 = do m <- getLine
           n <- getLine
           p <- getLine
           -- alternatively: [m, n, p] <- replicateM 3 getLine
           -- use a let statement
           let a = read m :: Int
           replicateM_ a (putStrLn n)
           replicateM_ a (putStrLn p)