首页 > 解决方案 > Haskell:如何在 do 块下使用 where 语句

问题描述

当我遇到一个我不明白的错误时,我正在用 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)

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

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 类型(一旦你在其中)才能使用字符串。但我不明白为什么“哪里”会“爆发”这种 IO 类型。据我了解,我给出的两个示例功能相同。首先,我认为编写没有 where 的程序无法修复错误,因为读取的函数来自类型read :: Read a => String -> a,而我在第一个程序中的输入也是IO String. 那么为什么我的第一个程序没有给我一个错误呢?有人可以解释我理解的错误以及如何修复我的程序以便我只需要执行read m :: Int一次吗?只是一些关于如何在 do 块下使用 where 语句的提示也会有所帮助。

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

标签: haskelliowhere-clause

解决方案


块中的绑定do对其后面的语句是不透明的where,因此您不能引用语句do内部块中定义的任何内容where。您也不需要,因为您可以let直接在内部使用do

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)

推荐阅读