不能在 where 块中使用“<-”吗?

Can't "<-" be used in a where block?

我基本上是在尝试在工作目录的 "res" 文件中定义给定文件的路径,我所做的如下:

iZone :: [Char]
iZone = "21.evt"

iZonePath :: String IO
iZonePath = result
 where
  path <- getCurrentDirectory
  result = (path ++ /res/ ++ iZone)

但是,ghc 符合

src/Main.hs:15:8: error:

parse error on input ‘<-’

Perhaps this statement should be within a 'do' block?    | 15 |   

path <- getCurrentDirectory

这是什么原因?我不能在 "where" 块中使用“<-”operation 吗?

IO monad 旨在强制程序员在副作用中选择特定的顺序。

假设这是允许的:

foo :: IO ()
foo = do
  putStrLn "1"
  putStrLn x
  putStrLn "2"
  putStrLn x
  putStrLn "3"
 where
  x <- putStrLn "x generated here" >> return "x used here"

输出结果是什么?

x generated here 是否打印在 do 块的最开始,在 1 打印之前?就在那之后?

x generated here是打印一次还是两次?

如果一开始就出现一次,那么简单写一下就清楚多了

foo :: IO ()
foo = do
  x <- putStrLn "x generated here" >> return "x used here"
  putStrLn "1"
  putStrLn x
  putStrLn "2"
  putStrLn x
  putStrLn "3"

如果要稍后执行,我们可以将其移动到想要的位置。

如果要运行两次,我们可以写成

foo :: IO ()
foo = do
  x1 <- generateX
  putStrLn "1"
  putStrLn x1
  putStrLn "2"
  x2 <- generateX
  putStrLn x2
  putStrLn "3"
 where
  generateX = putStrLn "x generated here" >> return "x used here"

(或者,为了提高可读性,使用 let 而不是 where

在上面的代码中,很明显 generateX 在精确的点上被 运行 两次。

允许where x <- ...使执行不明确。相反,do 使其精确。

事实上,do 块的整个语法可以根据单子操作 >>=return 重写,这保证了 IO 操作的明确顺序。

原则上,可以扩展 Haskell 并定义 where x <- ...,至少在某些情况下允许这样做,但这样做似乎没有任何好处,而且代码清晰度也会有所损失.