where 用于将变量绑定到新值,如
f x = ....
where n = ...
m = ...
g y = ...
特别是不能运行IO动作的地方:最多可以定义e.g. m 是这样的一个动作。 Haskell 类型系统基本上强制您指定 IO 操作之间的顺序,通常使用 do 完成。如果 Haskell 允许 where do g <- ...,则不清楚何时运行此操作相对于其他操作:例如,
f x = do print 1
print 2
print n
print 4
where n <- do print "hello!" -- hypothetical Haskell syntax
return 3
可能意味着
f x = do print 1
n <- do print "hello!"
return 3
print 2
print n
print 4
还有
f x = do print 1
print 2
n <- do print "hello!"
return 3
print n
print 4
回到您的代码,您可以简单地将其更改为单个do:
pair :: IO String
pair = do g <- newSTdGen
let n = randomR (1,10) g
return (randSeq n ++ " | " ++ randSeq n)
顺便说一句,有一个库函数randomRIO 可以生成相同的数字,所以你可以简单地使用
pair :: IO String
pair = do n <- randomRIO (1,10)
return (randSeq n ++ " | " ++ randSeq n)
请记住,上面生成了一个随机数,并使用了两次。如果您打算使用两个独立的随机数,请改用
pair :: IO String
pair = do n1 <- randomRIO (1,10)
n2 <- randomRIO (1,10)
return (randSeq n1 ++ " | " ++ randSeq n2)
最后,上面的代码假定randSeq 返回一个String 而不是IO String。如果它确实返回了 IO String,那么您还必须订购这两个操作。
pair :: IO String
pair = do n1 <- randomRIO (1,10)
n2 <- randomRIO (1,10)
s1 <- randSeq n1
s2 <- randSeq n2
return (s1 ++ " | " ++ s2)
或者,使用一些库函数来美化代码,
pair :: IO String
pair = do s1 <- randomRIO (1,10) >>= randSeq
s2 <- randomRIO (1,10) >>= randSeq
return (s1 ++ " | " ++ s2)
甚至
pair :: IO String
pair = do s1 <- randomSeq
s2 <- randomSeq
return (s1 ++ " | " ++ s2)
where -- this does not run the IO action, it only gives it a name
randomSeq = randomRIO (1,10) >>= randSeq
最后,pair 不能在没有 IO monad 的情况下返回 String,因为它不是一个常量字符串,而是每次运行时都会生成一个随机字符串的动作。