关于一种新型Perl/CORE系统的思考

2020-07-12 19:29:19

COR需要一个类型系统。但是,这可能会出现在v2中,因为事实证明a)很难,b)我们需要类型用于:

变量声明没有太多的讨论,而Dave Mitchell在签名类型方面的工作早于Cor在声明类型方面的尝试,所以它是不兼容的。我已经在Perl git repo上打开了一张关于类型的门票。我们绝对不能有一个COR的类型系统,因为它没有集成到语言的其余部分中。

不幸的是,因为数据是类型化的,所以我们倾向于将类型系统和数据类型混为一谈。人们通常认为声明Int或Str就是类型系统。这不是真的。出于本次讨论的目的,我将借用本杰明·C·皮尔斯的定义,他的优秀著作《类型和编程语言》(Types and Programming Languages:

类型系统是一种易于处理的语法方法,通过根据短语计算的值的种类对短语进行分类,来证明某些程序行为是不存在的。

类型系统的设计目标应该是确保软件不会表现出某些不需要的行为。

因此,如果我们声明一个变量必须包含DateTime对象的一个实例,那么将字符串";foo";赋给它应该会抛出异常。它应该在编译时还是在运行时这样做是一个有很大争议的主题,我将在很大程度上跳过这一点,除了提到-对于Perl-编译时检查应该是首选的,因为这样做不会让我们的编程工作变得更加困难。我也喜欢渐进式打字,所以我们可以通过快速编码来解决眼前的问题,但我们也可以根据需要对大型系统采取更严格的限制。

这就是说,让我们考虑一下污点检查。污点检查的作用比大多数开发人员意识到的要多,但最常见的行为是不使用程序外部的数据来影响程序内部的东西,除非您清除了外部数据(显然,这是一种粗略的过度简化)。否则,你会失败的。这在运行时发生:

$1Perl-te';My$arg=Shift;说";1.$arg";system($arg);说";2;$arg";';foo1。foo在-e行1上使用-T开关运行时,$ENV{PATH}不安全。

因此,污点检查是一种类型系统,因为可以避免某些不需要的行为。

启用限制(使用严格)就是启用另一个类型系统,其中包括以某种方式强制预声明非包变量。此故障在编译时发生:

$1Perl-Mstrict-E&39;my@foo=(1,2);say";Hello";;say%foo';全局符号";%foo";需要明确的包名称(您是否忘记声明";my%foo";?)。at-e行1.由于编译错误,-e的执行被中止。

因此,Perl开发人员对编译类型失败相当满意,即使他们并不总是认识到";类型的含义比通常认为的更重要。

因此,对于Cor(以及一般的Perl)来说,值得问问自己,我们未来需要什么样的类型系统。

这些内容摘自前面提到的“类型和编程语言”一书的介绍。我将使用COR类型语法作为示例。

类点:def__init__(self,x,y):self。x=x自身。y=y定义反转(自):返回点(自。Y,赛尔夫。x)点=点(7,3.2)。Inverse()打印(点。x)点。x=#34;foo&34;打印(点。x)。

这将打印3.2&34;和#34;Foo&34;。在Python中,考虑使用非Python语言来验证数据。当面对一百万行的代码库时,你应该只知道你所有的类。这真是不太好。

包点{sub new{my($class,$x,$y)=@_;Return blefits{x=&>;$x,y=>;$class;}sub x{my$self=Shift;if(@_){$self-&&>;{x}=Shift;}return$self-&>;{x};}sub y{my$self=Shift;if(@_){$self-&>;{x};}sub y{my$self=Shift;if(@_){$self-&>;if(@_){$self-&&>t;{y}=Shift;}返回$Self->;{y};}小逆{My$Self=Shift;Return Point-&>;new($Self->y,$Self-&>x);}}。

幸运的是,开发人员已经厌倦了相关的错误,因为代码应该尽可能地保护程序员。在COR,情况大概是这样的:

类点{Has($x,$y):Reader:Writer:New:ISA(Num);方法invert(){return Point->;new(x=>;$y,y=>;$x);}}。

如果您试图将字符串";foo";赋给x或y,您会得到一个异常。

这里的安全设施应该是什么?你应该得到-3.5吗?这就是标准Perl会做的事情,因为它尽量不丢弃信息。对于某些语言,这将返回-3或-4,两者都是错误的。对于Perl中的类型系统,我建议我们在那里得到一个异常(或至少一个警告)。我不希望浮点数自动转换为整数,也不希望更改类型。

我们要强迫它变成一个整数吗?不幸的是,是的,因为上下文的原因。但是,如果我们谈论的是函数的返回值,那就更复杂了:

子列表{return(2,4,6);}子数组{my@array=(2,4,6);return@array;}my$list=list();my$array=array();说$list;说$array;

Say$List打印6,Say$array打印3,这是由于上下文与列表和数组(它们是列表的容器,尽管这个容器有一个糟糕的习惯,即递给您一个裸列表然后逃跑)的奇怪组合造成的。

如何在不破坏上下文的情况下设计类型安全系统呢?

在一种没有明确为人设计的语言上改造类型系统充满了陷阱。

在上面,我们可以阅读我们的代码,并对我们需要的数据类型有一个合理的想法。与POD或其他形式的文档不同,此文档不能与程序不同步,因为它是程序。此外,像javadoc这样的工具仅通过阅读这些信息就可以生成大量各种格式的真正有用的文档。如果Perldoc的未来版本可以利用这一点,那就太好了。

类型系统通常可以保证编程中的各种安全性。Perl&39;s&34;污点模式就是这样一种类型的系统。但是,数组边界检查是另一种方法。例如,C语言要求您自己检查数组边界。其他语言可以在编译时(比听起来更难)或运行时抛出越界异常,而Perl只是动态调整数组大小以避免溢出错误。

不过,还有其他类型的安全可以实现。设想一个Secret[]修饰符(此示例不是认真建议的):

现在,在打印$PASSWORD的任何地方,结果字符串都将是hunter2。堆栈追踪?他们会看到猎人2。是否在命令行打印?猎人2?在字符串中插入?猎人2.。

在COR中,如果将类声明为Secret,则可能会有效地添加如下所示的方法:

任何在子类或角色中覆盖它的尝试都将是致命的。

也许我们可以更进一步,防止该数据被写入交换?是否在变量超出作用域时自动清除该内存?使用一个像样的类型系统,您可以做各种安全的事情。

但是如何以安全的方式获取原始信息呢?假设您需要加盐,在上面运行河豚,然后将结果保存到数据库中。如果您将Secret[]设置得太安全,它将无法使用。但是如果你把它弄得太好用,它就不安全了。了解了我可以在Perl中使用名称空间的有趣把戏,我怀疑大多数保护数据的天真方案都会失败。尽管如此,这正是一个像样的类型系统可以改进的那种安全性(尽管不能保证)。

这可能需要更多时间,但这是一个明确的奋斗目标。如果我将一个类型声明为整数,那么Perl将不再需要将大量内部内容存储在标量中。或者至少,它将始终从IV插槽返回有效值,并且永远不会检查标量中的另一个插槽。然而,我预计第一个实现实际上会减慢Perl的速度,因为嘿,您必须检查类型!提供一些在详尽测试后关闭类型检查然后交付生产的方法将不是一个好主意,但是有人会提议这样做。

在最初的C版本中(甚至在它发布之前),唯一的类型是整数、字符、整数数组、字符以及指向它们的指针。类型并不是为了帮助人类,而是为了帮助计算机。然而,随着面向对象系统的出现,您可以将某些东西声明为类的实例。因此,类型系统变得更人性化了,因为类型符合程序员的需求,而不仅仅是计算机的需求,尽管我们有这样的东西:

COR也应该是对人类友好的,尽管对我来说,实现这一点的最佳方式还不清楚。我们可以实现某种形式的泛型:

这将把它得到的类型赋给$your_Bike,但是我不清楚去掉类型注释会给我们带来什么好处。

但我预计很多Perl开发人员会对此感到不满,我就是其中之一。我已经非常努力地限制在语言中添加新的语法。欢迎就如何在不创建过于冗长的语言的情况下改进打字提出建议。

然而,我们有很多案例是我们的类型几乎足够好,但不完全是。如果我想要一个整数,但它必须大于给定的数字,该怎么办?那就是我们从Raku偷东西的地方:

再一次,我们得到了一个更具表现力的字体系统,它适合人类的需要,而不是计算机的需要。当然,我们可以实现一个MinimumAge类,但在许多情况下这都是矫枉过正的。

我们想在COR上使用泛型吗?乍一看,我们可以简单地省略类型。我已经看过各种示例,只要我们传递给某个对象的数据是类型化的,省略类型注释就足够了。然而,我希望看到反例。

是的,我们需要考虑类型层次结构。我认为慕斯的那个是合理的,但这不是我的专业领域。