像这样的方法引用Float.times在Ceylon中表示为折叠
形式。我可以写
Float twoTimes(Float x) = 2.times;
这里,表达式2.times是方法times()对接收器表达式2.
的部分应用产生的典型一等函数引用
Float times(Float x)(Float y) = Float.times;
但实际上,表达式Float.times是方法声明的一个元模型引用
。类型Method<Float,Float,Float>是Callable<Callable<Float,Float>,Float>的子类型,因此我们可以将其视为函数引用。
因此,twoTimes()的另一个定义是
Float twoTimes(Float x) = Float.times(2);
(我们通过提供其两个参数列表中的一个来部分应用)Float.times不幸的是,以下代码没有正确类型
问题在于
Float product(Float x, Float y) = Float.times; //error: Float.times not a Callable<Float,Float,Float>
,当作为一个函数引用来考虑时,它是一个接受Float.timesFloat并返回接受的函数的高阶函数并返回接受,而不是接受两个并返回接受s 的一阶函数。
那么我们如何将方法引用Float.times转换为只有一个参数列表的非折叠
函数呢?
好吧,一个真正简单的方法就是回退到编写
Float product(Float x, Float y) { return x.times(y); //or even: x*y }
但是,嗯,这篇文章的目的是展示Ceylon中高阶函数的一些高级特性,所以这不是一个非常有趣的解决方案。相反,我们将使用一个真正酷的高阶函数,它将成为Ceylon语言模块的一部分。这只需要两行代码,所以我相信你马上就能理解它
R uncurry<R,T,P...>(R curried(T t)(P... p))(T receiver, P... args) { return curried(receiver)(args); }
哇!Wtf?
显然,你需要重新阅读第8部分!好吧,已经读完了吗?酷,现在让我们来展开这个
- 首先,它是一个具有两个参数列表的函数,所以uncurry()()是一个返回函数的函数。
- 第一个参数列表包含一个具有两个参数列表的单个参数,所以参数curried()()也是一个返回函数的函数。
- curried()()具有以下形式的参数:P...,是一个 有序类型参数,因此我们知道curried()()是针对具有任意参数列表的函数进行某种抽象。
- 第二个参数列表包含两个参数,与curried()()的参数列表中的单个参数的类型相同。这些是uncurry()().
返回的函数的参数。 uncurry()()执行的操作是接受一个以柯里化形式存在的函数,其中第二个参数列表可以有任意数量的参数,并生成一个只有一个参数列表的函数,包括参数函数的所有原始参数。这是curried()()将
Float product(Float x, Float y) = uncurry(Float.times);
的参数列表“扁平化”为单个参数列表。因此,我们可以编写以下内容:
R curry<R,T,P...>(R uncurried(T t, P... p))(T receiver)(P... args) { return uncurried(receiver,args); }
以类似的方式表示函数的其他类型操作。考虑以下函数:uncurry()()此函数正好与
Float times(Float x)(Float y) = curry(product); Float double(Float y) = times(2.0);
相反,它接受参数函数的第一个参数,并将其分离成自己的参数列表,允许参数函数部分应用
R compose<R,S,P...>(R f (S s), S g(P... p))(P... args) { return f(g(args)); }
现在考虑以下函数:
Float incrementThenDouble(Float x) = compose(2.0.times,1.0.plus);
幸运的是,您不需要自己编写像curry()(), uncurry()()和compose()()这样的函数。它们是作为语言模块的一部分打包的通用工具。尽管如此,了解像这样的机制可以在Ceylon的类型系统中表达出来是很不错的。