在唤醒代码中,我知道def
表示某物是一个函数。但是,似乎还有另一个关键字target
也定义了函数。例如(taken from here):
global target makeBitstream plan =
...
global def makeMCS plan =
...
这两个都可以从命令行调用。 def
和target
有什么区别?
答案 0 :(得分:1)
我刚刚写了一篇关于这个主题的文章:
虽然唤醒语言主要是功能性的,但它并不是完全纯净的。唤醒具有诸如运行作业和打印之类的副作用。它还具有备忘录,这是一种存储计算的方法,如下所述。
考虑斐波那契函数:
def fib n = if n < 2 then 1 else fib (n-1) + fib (n-2)
在20上评估此功能相对较快完成,但在40上运行则完全是另一回事。唤醒似乎永远持续(真的,很长一段时间)。
问题在于,每次调用fib都会导致两次以上的fib调用。这导致了连锁反应,根据输入的信息,fib被称为指数增加。
让我们尝试特殊的目标关键字:
target fib n = if n < 2 then 1 else fib (n-1) + fib (n-2)
现在fib 40快速完成!实际上,fib 40000也会返回结果。
这里发生的事情是目标fib现在可以记住并重新使用先前调用的结果。 fib 4将调用fib 3和fib2。fib3将调用fib 2和fib1。但是,现在对fib 2的普通调用仅发生一次。目标记住结果;这称为记忆。
虽然目标对于加速玩具功能很有用,但其实际用途是在构建系统中节省工作。唤醒构建系统通常包括构建规则,该构建规则调用它们所依赖的其他规则。想象一下,作业C取决于作业B和A,但是作业B也取决于作业A。我们不希望A执行两次!
幸运的是,唤醒的Plan API默认包含一次运行作业的选项。在内部,此API使用目标来防止作业的重新执行。但是,这种使用target并不能满足大型构建的需求。
在大型构建中,通常应使用target定义为其他功能生成Path的顶级功能。这样,即使该函数被依赖项调用了两次,也只需对其求值一次。就像我们在fib示例中看到的那样,在涉及多个目标且依赖多个目标的构建中,结果可能是指数级加速。
目标也可以在函数内部定义。这些目标仅保留其保存的值,而封闭功能可以访问它们。
def wrappedFib n =
target fib n = if n < 2 then 1 else fib (n-1) + fib (n-2)
fib n
在此示例中,wrapedFib使用内部目标fib计算斐波那契结果。但是,在包装的Fib调用之间,不保留部分结果。嵌套目标非常有用,因为它们在整个唤醒过程中不会占用内存。例如,一个函数可能需要计算大量不感兴趣的中间值,以便计算感兴趣的值(可以保存)。
考虑目标的一种方法是,它定义一个表,例如在数据库或键值映射中。例如,目标foo x y = z定义了一个表,键为Pair x y,值为z。从这个角度来看,使用某些不属于键的输入来计算z有时会很有用。
目标myWrite文件名\内容=写入文件名内容 在上面的示例中,myWrite“ file”“ content”将创建一个名为file的文件,并用字符串content填充该文件,并为创建的文件返回一个Path。如果有人尝试再次写入具有相同内容的相同文件,则将返回相同的路径。
但是,如果有人尝试写入相同的文件却具有不同的内容怎么办?如果我们允许的话,构建系统中将存在竞争条件!让我们看看会发生什么:
$ wake -x '("bar", "bar", Nil) | map (myWrite "foo")'
Path "foo", Path "foo", Nil
$ wake -x '("bar", "baz", Nil) | map (myWrite "foo")'
ERROR: Target subkey mismatch for 'myWrite filename \ contents' (demo.wake:1:[8-34])
在第一次调用中,两个调用均成功,并且foo仅创建一次。在第二次调用中,其中一个调用失败,并且目标子项不匹配。这些失败在唤醒后是致命的,因为由于唤醒所使用的乱序并行评估策略,永远无法确定哪个调用失败。尽管如此,如果越野车的建造失败得比一般的偶然失败要好得多。
最后,被警告这个共同的目标陷阱:
target foo x = match _
None = None
Some y = x + y
通常在唤醒时,对于定义(def),上述功能与此相同:
target foo x y = match y
None = None
Some y = x + y
但是,在目标情况下,这些是完全不同的。第一个目标foo表示一个函数结果,而第二个目标foo表示一个Integer结果(可能是预期的结果)。