我知道语言存在,但我不能指责它。
动态范围 和 静态打字?
答案 0 :(得分:1)
我们可以尝试推断这种语言的外观。显然这样的事情(使用类C语法进行演示)是不允许的,或者至少没有明显的含义:
int x_plus_(int y) {
return x + y; // requires that x have type int
}
int three_plus_(int y) {
double x = 3.0;
return x_plus_(y); // calls x_plus_ when x has type double
}
那么,如何避免这种情况?
我可以想到一些方法:
上面提到的Fortran pre-' 77就有这种行为。这是有效的,因为变量的名称决定了它的类型;像上面x_plus_
这样的函数是非法的,因为x
永远不会有整数类型。 (同样就像three_plus_
一样,因为y
会有相同的限制。)整数变量的名称必须以i
,j
,{{开头1}},k
,l
或m
。
Perl使用语法来区分几大类变量,即标量与数组(常规数组)与散列(关联数组)。属于不同类别的变量可以具有完全相同的名称,因为语法区分了哪一个。例如,表达式n
涉及函数foo $foo, $foo[0], $foo{'foo'}
,标量foo
,数组$foo
(@foo
是$foo[0]
的第一个元素),哈希@foo
(%foo
是与$foo{'foo'}
对应的%foo
中的值。现在,很明显,Perl 不是静态类型,因为有许多不同的标量类型,这些类型不在语法上有所区别。 (特别是:所有引用都是标量,甚至是对函数或数组或散列的引用。因此,如果使用语法来取消引用数组,Perl必须在运行时检查该值是否真的是数组引用。 )但是同样的方法可以用于真正的类型系统,特别是如果类型系统是一个非常简单的系统。使用该方法,'foo'
方法将使用x_plus_
类型x
,并将完全忽略int
声明的x
。 (相反,它将使用three_plus_
类型x
,必须从名为int
的任何范围提供。)这可能需要上面未包含的某些类型注释,或者它可以使用某种形式的类型推断。
函数的签名可以指示它使用的非局部变量及其预期类型。在上面的示例中,three_plus_
将具有签名"采用类型为x_plus_
的一个参数;使用int
类型的调用范围x
;返回类型int
"的值。然后,就像调用int
的函数必须传入类型x_plus_
的参数一样,它也必须提供类型int
的名为x
的变量 - 通过声明它本身,或者通过继承类型签名的那一部分(因为调用int
等同于使用类型x_plus_
的{{1}})并将此要求传播到< em>它的来电者。使用这种方法,上面的x
函数将是非法的,因为它会违反它调用的int
方法的签名 - 就像它试图传递three_plus_
一样参数。
以上可能只有&#34;未定义的行为&#34 ;;编译器不必明确地检测和拒绝它,但规范不会对它如何处理它施加任何特定的要求。程序员有责任确保他们永远不会使用错误输入的非局部变量来调用函数。
你的教授大概是在考虑#1,因为前#77 Fortran是一个具有这种属性的真实世界语言。但其他方法很有意思。 : - )
答案 1 :(得分:0)
我还没有在其他地方找到它写下来的,但是 AXIOM CAS(以及各种叉子,包括仍在积极开发中的 FriCAS)使用一种称为 SPAD 的脚本语言,具有非常新颖的强大功能静态依赖类型系统和动态范围(尽管它可能是一个意外的实现错误)。
大多数时候用户不会意识到这一点,但是当他们开始像其他函数式语言一样尝试构建闭包时,它揭示了其动态范围的性质:
FriCAS Computer Algebra System
Version: FriCAS 2021-03-06
Timestamp: Mon May 17 10:43:08 CST 2021
-----------------------------------------------------------------------------
Issue )copyright to view copyright notices.
Issue )summary for a summary of useful system commands.
Issue )quit to leave FriCAS and return to shell.
-----------------------------------------------------------------------------
(1) -> foo (x,y) == x + y
Type: Void
(2) -> foo (1,2)
Compiling function foo with type (PositiveInteger, PositiveInteger)
-> PositiveInteger
(2) 3
Type: PositiveInteger
(3) -> foo
(3) foo (x, y) == x + y
Type: FunctionCalled(foo)
(4) -> bar x y == x + y
Type: Void
(5) -> bar
(5) bar x == y +-> x + y
Type: FunctionCalled(bar)
(6) -> (bar 1)
Compiling function bar with type PositiveInteger ->
AnonymousFunction
(6) y +-> #1 + y
Type: AnonymousFunction
(7) -> ((bar 1) 2)
(7) #1 + 2
Type: Polynomial(Integer)
这种行为类似于在动态范围的 Lisp(例如 Emacs Lisp)中尝试使用 (lambda (x) (lambda (y) (+ x y)))
构建闭包时会发生的情况。实际上,函数的底层表示与早期的 Lisp 基本相同,因为 AXIOM 最初是在 IBM 大型机上的早期 Lisp 实现之上开发的。
我相信这是一个缺陷(就像 JMC 在实现 LISP 语言的第一个版本时发生的那样),因为实现者让解析器像 bar
的函数定义中那样做非柯里化,但这不太可能在没有能力用语言构建闭包的情况下很有用。
还值得注意的是,SPAD 会自动重命名异常函数中的变量以避免捕获,因此它的动态范围可以用作其他 Lisps 中的功能。