另请参阅我对how the yin yang puzzle works 的回答,我必须先找到答案才能回答这个问题。
作为一种“类型化”语言本身并不会影响这个谜题是否可以用它来表达(无论“类型化语言”这个词有多么模糊)。但是,从字面上回答您的问题:是的,这是可能的,因为 Scheme 本身就是一种类型化语言:每个值都有一个已知类型。这显然不是你的意思,所以我假设你的意思是在每个变量都被分配一个永不改变的永久类型的语言中这是否可能(又名“静态类型语言”)。
此外,我假设您希望以某种语言表达拼图的精神。显然可以用 x86 机器码编写 Scheme 解释器,显然也可以用只有整数数据类型和函数指针的 typed 语言编写 x86 机器码解释器。但结果不是同一个“精神”。所以为了更精确,我将提出一个额外的要求:结果必须使用真正的延续来表达。不是模拟,而是真正的全面延续。
那么,你能拥有一种带有延续的静态类型语言吗?事实证明你可以,但你仍然可以称之为作弊。例如,在 C# 中,如果我的延续被定义为“接受一个对象并返回一个对象的函数”,其中“对象”是一种可以容纳任何东西的类型,你会觉得这是可以接受的吗?如果函数接受并返回“动态”怎么办?如果我有一种“类型化”语言,其中每个函数都具有相同的静态类型:“函数”,而不定义参数类型和返回类型,该怎么办?生成的程序是否仍然具有相同的精神,即使它使用了真正的延续?
我的观点是“静态类型”属性仍然允许类型系统中的大量变化,足以产生所有差异。所以只是为了好玩,让我们考虑一下类型系统需要支持什么才能在任何情况下都被视为非作弊。
运算符call/cc(x)也可以写成x(get/cc),我觉得这样更容易理解。这里,x 是一个函数,它接受一个 Continuation 并返回一个值,而 get/cc 返回一个 Continuation。 Continuation 具有函数的所有特征;它可以用一个参数调用,并将传入的值替换为创建它的原始位置的 get/cc,另外在该点恢复执行。
这意味着 get/cc 有一个尴尬的类型:它是一个function,但同样的位置最终会返回一个我们还不知道其类型的值。然而,假设本着静态类型语言的精神,我们需要固定返回类型。也就是说,当您调用延续对象时,您只能传入预定义类型的值。使用这种方法,可以使用T = function T->T 形式的递归表达式来定义延续函数的类型。有朋友指出,这个类型其实可以用C#声明:public delegate T T(T t);!
所以你有它;被“打字”并不排除或保证你可以在不改变其性质的情况下表达这个谜题。但是,如果您允许静态类型“可以是任何东西”(在 Java 和 C# 中称为 object),那么您唯一需要的另一件事就是支持真正的延续,并且可以表示拼图没有问题。
从不同的角度处理同一个问题,考虑一下我将这个谜题改写成更让人想起传统静态类型命令式语言的东西,我在linked answer 中对此进行了解释:
yin = (function(arg) { print @; return arg; })(get-cc);
yang = (function(arg) { print *; return arg; })(get-cc);
yin(yang);
这里,yin 和 yang 的类型永远不会改变。他们总是存储一个“接受一个C并返回一个C的延续C”。这与静态类型非常兼容,静态类型的唯一要求是下次执行该代码时类型不会改变。