最近,我开始学习Haskell,因为我想拓展自己在函数式编程方面的知识,目前我正在使用Pluralsight上的“Haskell Fundamentals Part 1”课程资源。不幸的是,我有一些困难理解讲师关于以下代码的特定引用,并希望你们能够解释一下这个主题。
附带代码
helloWorld :: IO ()
helloWorld = putStrLn "Hello World"
main :: IO ()
main = do
helloWorld
helloWorld
helloWorld
引言
如果你在一个 do-block 中使用了相同的 IO 操作,它将会被运行多次。因此,这个程序会打印出字符串“Hello World”三次。这个例子有助于说明 putStrLn
不是一个具有副作用的函数。我们调用 putStrLn
函数一次来定义 helloWorld
变量。如果 putStrLn
具有打印字符串的副作用,它只会打印一次,而主要的 do-block 中重复出现的 helloWorld
变量则不会产生任何效果。
在大多数其他编程语言中,这样一个程序只会打印一次“Hello World”,因为打印会在调用 putStrLn
函数时发生。这种微妙的差别往往会让初学者犯迷糊,因此请认真思考一下,并确保你理解为什么这个程序会打印三次“Hello World”,以及如果 putStrLn
函数将打印作为副作用,则为什么只会打印一次。
我不明白的地方
对我来说,字符串“Hello World”被打印三次似乎是很自然的。我把 helloWorld
变量(或函数?)看作是一种稍后调用的回调。我不明白的是,如果 putStrLn
具有副作用,为什么它只会导致字符串被打印一次。或者为什么在其他编程语言中它只会被打印一次。
假设在 C# 代码中,我会认为它看起来像这样:
C# (Fiddle)
using System;
public class Program
{
public static void HelloWorld()
{
Console.WriteLine("Hello World");
}
public static void Main()
{
HelloWorld();
HelloWorld();
HelloWorld();
}
}
我确信我忽略了某些简单的东西或者误解了他的术语。非常感谢您的帮助。
编辑:
非常感谢大家的回答!你们的回答帮助我更好地理解了这些概念。我觉得还没有完全明白,但我将来会再次研究这个话题,谢谢!
helloWorld
就像是 C# 中的常量,例如字段或变量。没有任何参数被应用于helloWorld
。 - CaramirielputStrLn
没有副作用;它只是返回一个 IO 动作,对于参数"Hello World"
,无论你调用putStrLn
多少次,它都返回相同的 IO 动作。 - chepnerhelloWorld = Console.WriteLine("Hello World");
那样做。您只需将Console.WriteLine("Hello World");
包含在HelloWorld
函数中,以便每次调用HelloWorld
时执行。现在考虑一下helloWorld = putStrLn "Hello World"
是如何使helloWorld
成为一个IO单子的。它被分配给包含“()”的IO单子。一旦您通过>>=
将其绑定,它才会执行它的活动(打印某些内容),并在绑定运算符的右侧给出()
。 - Redu