Types 在Perl 6中创建类型

Types 在Perl 6中创建类型,types,raku,Types,Raku,我有很多函数可能会失败,但它们的签名中也定义了返回类型。因为我喜欢尽可能地定义变量的类型,所以我想定义一个用于此的子集。我想到的是: subset Maybe is export of Mu where Mu | Failure; use nqp; class Maybe { method ^parameterize(Mu:U \M, Mu:U \T) { Metamodel::SubsetHOW.new_type: :name("Maybe[

我有很多函数可能会失败,但它们的签名中也定义了返回类型。因为我喜欢尽可能地定义变量的类型,所以我想定义一个用于此的子集。我想到的是:

subset Maybe is export of Mu where Mu | Failure;
use nqp;

class Maybe {
    method ^parameterize(Mu:U \M, Mu:U \T) {
        Metamodel::SubsetHOW.new_type:
            :name("Maybe[{T.^name}]"),
            :refinee(nqp::if(nqp::istype(T, Junction), Mu, Any)),
            :refinement(T | Failure)
    }
}

my Maybe[Int] $foo = 1;
say $foo; # OUTPUT: 1
my Maybe[Int] $bar = Failure.new: 2;
say $bar.exception.message; # OUTPUT: 2
my Maybe[Int] $baz = 'qux'; # Throws type error
这个问题是
Failure
Mu
的一个子类,所以这将匹配任何东西,而我真正想要的是能够动态地匹配一个特定类型以及
Failure
。我的下一个想法是创建一个参数化角色作为一个类型使用,因为我不想为每一个类型创建子集,这也可能是一个
失败
。我想象它看起来像这样:

role Maybe[::T] {
    # ...
}

sub foo(--> Int) { rand < 0.5 ?? 1 !! fail 'oops' }

my Maybe[Int] $foo = foo;
角色可能[::T]{
# ...
}
sub foo(-->Int){rand<0.5±1!!失败'oops'}
我的Maybe[Int]$foo=foo;

只是我不知道我需要为这个角色增加什么才能让它发挥作用。有可能创建这样一个角色吗?如果没有,是否有其他方法可以创建一个类型来执行我想要的操作?

因为6个类型已经是可能的类型了

只是Perl6的类型为null,这与大多数其他语言的类型不同


这是
可能[Int]
变量:

my Int$a;
我的智力:$a;#更明确
这包含一个明确的
Int

my Int:D$a=…#必须指定,因为默认值不是“确定的”
这将保存一个空的
Int

my Int:U$a;

请注意,
Failure
Nil
的一个子类型,因此即使指定了返回类型的子例程也可以返回它们。
Nil
与其他语言中的
null
Nil
不同。)

subfoo(-->Int:D){Bool.pick±1!!失败'oops'}
我的$foo=foo;#$foo包含failure对象
Nil
实际上是一种通用的软故障。当分配给变量时,它只是将其重置为默认值

my Int$foo=1;
$foo=零;
比如$foo.perl;#Int
my Int:D$bar为默认值(42)=1;
$bar=零
比如$bar.perl;#42
典型的默认值与类型相同

my Int$foo;
比如$foo.VAR.default.perl;#Int
一个特定的软故障是返回类型对象

sub foo(-->Int){
Bool.pick±1!!Int
}
这就是为什么我说
Nil
是一种“通用”软故障


通常,如果要定义变量的类型,则需要该类型的变量。因此,如果代码得到另一种类型的东西,它应该立即抱怨

有更好的方法来处理
故障

subfoo(-->Int:D){rand<0.5±1!!失败'oops'}
使用foo()->Int:D$foo{
…#在这里使用$foo
}else->$fail{
…#在此处使用$fail
}
这是因为
Failure
总是认为自己未定义

给定的foo(){
当Int:D->Int:D$foo时{
…#在这里使用$foo
}
当失败:D->Failure:D$fail时{
#确定的失效值
#('DEFINITE'与'defined'不同。)
}
违约{
…#处理意外值(如果不需要,可以删除)
}
}
或者,如果您不关心它是什么类型的故障,则只需定义或运算符
/

my Int:D$foo=foo()//1;
您甚至可能希望使用它将
故障
转换为
Nil

my Int:D$foo是默认值(42)=foo()//Nil;

如果你真的想要一个可能失败的子集,我认为这应该是可行的:

sub可能故障(任意:U::Type){
anon子集::任何where类型的|故障
}
我的常数Maybe Int=Maybe Failure(Int);
#请注意,此行的类型必须在编译时已知:
我的可能整数$foo=foo;
不过,它目前不起作用

(请注意,您不应该处理
Mu
,除非您需要专门处理
Any
类型之外的类型和值;例如
Junction
IterationEnd

其他一些可能也会起作用的方法是:

我的课可能会失败{
方法^parameterize($,Any:U::Type){
anon子集::任何where类型的|故障
}
}
我的可能失败[Int]$foo;
这似乎与另一个失败的原因相同


另一种方法是创建一种新类型的类,如
子集

也就是说,subset
使用的MOP与Perl6中的其他类不同。

为我指明了正确的方向,特别是:

另一种方法是创建一种新类型的类,如子集。 也就是说,子集使用的MOP与Perl6中的其他类不同

我提出的解决方案是:

subset Maybe is export of Mu where Mu | Failure;
use nqp;

class Maybe {
    method ^parameterize(Mu:U \M, Mu:U \T) {
        Metamodel::SubsetHOW.new_type:
            :name("Maybe[{T.^name}]"),
            :refinee(nqp::if(nqp::istype(T, Junction), Mu, Any)),
            :refinement(T | Failure)
    }
}

my Maybe[Int] $foo = 1;
say $foo; # OUTPUT: 1
my Maybe[Int] $bar = Failure.new: 2;
say $bar.exception.message; # OUTPUT: 2
my Maybe[Int] $baz = 'qux'; # Throws type error

TL;请参阅@Kaiepi博士自己的答案,以获得解决方案。但是P6中的每个非本机类型都已经自动成为一个增强的可空类型,它类似于一个增强的可空类型。所以这也需要讨论。为了帮助构建我的答案,我将假装它是,即使它不是

解Y 我想定义一个
可能的子集
,用于此

见@Kaiepi的答案

所有非本机P6类型都已经类似于Maybe类型
子集
解决方案对于定义为Maybe类型的解决方案来说是过度的,它可以归结为:

None
[或]原始数据类型

事实证明,所有非本机P6类型都已经类似于增强型P6类型

增强是(P6相当于a)
None
知道它与什么原始数据类型配对:

my Int $foo;
say $foo        # (Int) -- akin to an (Int) None
解X 我有很多函数可能会失败,但它们的签名中也定义了返回类型

您可能知道,除非是有效的,否则P6故意允许例程返回失败,即使有返回类型检查
my int $foo;
say $foo;    # 0
$foo = int;  # Cannot unbox a type object (int) to int