Perl 6 类型推断

Perl 6 type inference

给定:

sub abc(Int $n) { say $n }

如果我们将类型为 Str 的变量传递给 abc,我们会得到一个编译时错误:

my Str $s = "123";   abc $s;

如果我们传递一个包含字符串的无类型变量,我们会得到一个运行时错误:

my $s = "123";   abc $s;

C++ 支持允许类型推断的 auto 关键字。

是否可以在 Perl 6 中定义类似于 auto 的东西,这样:

auto $s = "123";

会导致 $s 被键入为 Str?

即然后我们可以执行以下操作:

auto $s = "123";   abc $s;

并得到编译时错误。

Is it possible to define something like auto in Perl 6

我认为是,有点。但我必须将您的问题翻译成三个不同的问题,以提供更有用的答案。

下面我对前两个问题的回答,请参考a reddit comment I wrote about this。我的评论分为四部分,用横线隔开。第一部分和最后一部分与您的问题相关。

是否可以修改 Perl 6 语言?

是的。

您可以或多或少地从用户空间代码中任意修改 Perl 6 语言。

引用我上面链接的评论第一部分的要点:

  • 使用语法构建的内部俚语 DSL,如外部 DSL,但通过俚语机制与标准 Perl 6 语法混合。这些 DSL 必须编写成至少与标准 Perl 6 语法有一些基本的协调。用这种 DSL 编写的代码看起来是内联的,与常规代码没有区别。 Relevant slides.

引用我写的最后一部分:

虽然可以使用俚语……但它们并未得到官方支持……并且没有它们需要的那么甜。

是否可以编写与编译器通信的编译时宏?

是的。

遗憾的是,您还不能定义语句宏,而这是您需要的一种新型变量声明。引用我上面链接的评论的最后一部分:

"The intended design options for situations where you want an internal DSL but it's too syntactically in conflict with standard Perl 6 to just use user defined routines and operators are ... Write an is parsed macro [or] Write a slang. Either of these can, in theory, parse any syntax you want. But is parsed macros haven't landed and look like they're at least a year or three away -- they're still just a design concept being discussed in 007."

是否有可能将新想法融入 Perl 6?

是的。

只需实施您的想法并游说其加入即可。可能从俚语开始。可能只是直接破解整个编译器。

最后一点我是认真的。

Rakudo Perl 6 编译器当然是开源的。

更重要的是,它主要是 written in Perl 6。 (大部分不是 Perl 6 的是 nqp,它基本上是完整 Perl 6 的一个子集。)

结论

如果你能写一些基本的 Perl 6 代码;愿意ask loads of questions on the freenode IRC channel #perl6-dev;并且有耐心;那么您应该能够在 Perl 6 中实现几乎任何想法,并且您的 auto 想法似乎相对简单。

(到 "relatively" 我想象一个知识渊博的核心开发人员可能能够在几个小时内完成概念验证,假设我已经正确理解他们将面临的问题。有一天,当俚语 and/or 宏更加完善时,可能需要大约一个小时。)

你要找的大概就是我们所说的allomorphs in Perl6, types that can behave differently depending on the context. For the particular case you have posted, there's the IntStr allomorph:

sub abc(Int $n) { say $n }
my IntStr $s = <123>;
abc $s; # prints 123

(请注意它需要 quote-words <> 而不是通常的 "")。一般来说,异形体是 类 的子类,它们可以被转换成; IntStr 子类 IntStr。只要它们可以相互转换,就可以对您想要 auto 的任何类型执行此操作。

另一个选择,如果你已经知道你将得到什么以及它会变成什么,那就是 use coercion:

sub bcd(Int(Str) $n) { say $n }
my $t = "123";
bcd $t; # prints 123

在这种情况下,字符串将在 Capture 中被强制转换为 Int,然后再被子程序主体使用。

最后,你可以使用the number contextualizer,只要你在变量将被有效使用时使用。

sub efg($n) { say +$n }
my Int $u = 123;
my Str $v = "123";
efg $u;
efg $v;

+ 将变量置于数字上下文中,自动将其转换为数字。如果您想将其限制为 Int 或 Str

,您甚至可以将其变成签名约束
sub xyz($n where $_ ~~ Int | Str ) { say +$n }
xyz $u;
xyz $v;
xyz $t;
xyz $s;

(它使用上面定义的变量)这里无论变量是否没有分配类型(如$t,或已定义为IntStr,[=16],它都会起作用=] 或 Str)。只要能智能匹配到IntStr,就会被送入sub body,通过语境化变成数字。

完整程序是here,以备您下载查看。

我只想提一下Proxies。这些在容器级别起作用,并且仅在 运行 时间起作用。但是要提供一些控制或跟踪变量内容的能力。

sub auto($v?) is rw {
    my $val = $v;
    Proxy.new(
        FETCH => -> $, { $val },
        STORE => -> $, $new-val {
            die "You canna change type from {$val.WHAT.^name} to {$new-val.WHAT.^name}"
                unless $new-val.WHAT ~~ $val.WHAT;
            $val = $new-val;
        }
    )
}

my $i := auto(11);
$i = 69;
say $i;
$i = 'blah'; # Boom

Proxy 可用于实现类似于 auto 类型的东西,但仅限于 运行-time 且仅限于容器级别。它们也不是那么防水,因为与变量关联的容器可以改变。例如,使用绑定 := 运算符

$i := 'he he'; # changed container