如果函数是完全独立的并且在调用另一个函数时不使用其中一个函数的结果,则无法按照您的描述进行操作。
这是因为没有理由这样做。在无副作用的设置中,调用一个函数然后忽略其结果与在调用该函数所需的时间内什么都不做(留出内存使用量)完全相同。
可能seq x y 将评估x,然后评估y,然后将y 作为结果,但不能保证此评估顺序。 p>
现在,如果我们确实有副作用,例如如果我们在 Monad 或 Applicative 中工作,这可能很有用,但我们并没有真正忽略结果,因为有上下文被隐式传递。例如,你可以这样做
main :: IO ()
main = putStrLn "Hello, " >> putStrLn "world"
在 IO Monad 中。另一个例子是 Monad 列表(可以认为是表示不确定性计算):
biggerThanTen :: Int -> Bool
biggerThanTen n = n > 10
example :: String
example = filter biggerThanTen [1..15] >> return 'a' -- This evaluates to "aaaaa"
请注意,即使在这里我们也没有真的忽略结果。我们忽略了具体的值,但我们使用了结果的结构(在第二个例子中,结构是filter biggerThanTen [1..15]的结果列表有5个元素)。
不过,我应该指出,以这种方式排序的事物不一定按照它们编写的顺序进行评估。您可以通过列表 Monad 示例看到这一点。这在更大的例子中变得更加明显:
example2 :: [Int]
example2 =
[1,2,3] >>=
(\x -> [10,100,1000] >>=
(\y -> return (x * y))) -- ==> [10,100,1000,20,200,2000,30,300,3000]
这里的主要内容是评估顺序(在没有像 IO 和忽略底部这样的副作用的情况下)不会影响 Haskell 中代码的最终含义(除了可能的效率差异,但这是另一个话题)。因此,永远没有理由以问题中描述的方式“一个接一个地”调用两个函数(也就是说,调用完全相互独立)。
做符号
Do 表示法实际上完全等同于使用>>= 和>>(实际上还涉及另一件事来处理模式匹配失败,但这与手头的讨论无关)。编译器实际上采用 do 表示法编写的内容,并通过称为“去糖”的过程将它们转换为 >>= 和 >>(因为它删除了语法糖)。以下是上面用 do 表示法编写的三个示例:
IO 示例
main :: IO ()
main = do
putStrLn "Hello, "
putStrLn "World"
第一个列表示例
biggerThanTen :: Int -> Bool
biggerThanTen n = n > 10
example :: String -- String is a synonym for [Char], by the way
example = do
filter biggerThanTen [1..15]
return 'a'
第二个列表示例
example2 :: [Int]
example2 = do
x <- [1,2,3]
y <- [10,100,1000]
return (x * y)
以下是转换的并排比较:
do --
m -- m >> n
n --
do --
x <- m -- m >>= (\x ->
... -- ...)
理解 do 表示法的最佳方法是首先理解 >>= 和 return,因为正如我所说,这就是编译器将 do 表示法转换成的内容。
作为旁注,>> 与>>= 相同,它只是忽略了左参数的“结果”(尽管它保留了“上下文”或“结构”)。所以>>的所有定义都必须等价于m >> n = m >>= (\_ -> n)。
在第二个列表示例中扩展>>=
为了帮助理解 Monad 通常不是不纯的这一点,让我们扩展第二个列表示例中的 >>= 调用,使用列表的 Monad 定义。定义是:
instance Monad [] where
return x = [x]
xs >>= f = concatMap f xs
我们可以将example2 转换为:
第 0 步(我们已有的)
example2 :: [Int]
example2 =
[1,2,3] >>=
(\x -> [10,100,1000] >>=
(\y -> return (x * y)))
第 1 步(转换第一个 >>=)
example2 =
concatMap
(\x -> [10,100,1000] >>=
(\y -> return (x * y)))
[1,2,3]
第 2 步
example2 =
concatMap
(\x -> concatMap
(\y -> return (x * y))
[10,100,1000])
[1,2,3]
第 3 步
example2 =
concatMap
(\x -> concatMap
(\y -> [x * y])
[10,100,1000])
[1,2,3]
所以,这里没有魔法,只是正常的函数调用。