我有很多可能会失败的函数,但是在其签名中定义了一个返回类型。由于我喜欢尽可能地定义变量的类型,因此我想定义一个Maybe子集用于此目的。我想到的是:
subset Maybe is export of Mu where Mu | Failure;
Run Code Online (Sandbox Code Playgroud)
这个的问题Failure是的子类Mu,因此当我真正想要的是能够Failure动态地匹配一种特定类型时,它将匹配所有内容。我的下一个想法是创建一个用作类型的参数化角色,因为我不想为也可以是的每个单个类型创建子集Failure。我想象它看起来像这样:
role Maybe[::T] {
# ...
}
sub foo(--> Int) { rand < 0.5 ?? 1 !! fail 'oops' }
my Maybe[Int] $foo = foo;
Run Code Online (Sandbox Code Playgroud)
只有我不知道要完成这项工作需要添加什么角色。是否可以创建这样的角色?如果不是,是否还有另一种方法可以创建类型以执行所需的操作?
Perl6类型已经是Maybe类型。
仅仅是Perl6键入了null,这与大多数其他具有Maybe类型的语言不同。
这是Maybe[Int]变量:
my Int $a;
my Int:_ $a; # more explicit
Run Code Online (Sandbox Code Playgroud)
这有一个确定的Int:
my Int:D $a = …; # must be assigned because the default is not “definite”
Run Code Online (Sandbox Code Playgroud)
它为null Int:
my Int:U $a;
Run Code Online (Sandbox Code Playgroud)
请注意,这Failure是的子类型Nil,因此即使指定了返回类型的子例程也可以返回它们。
(Nil不像null或nil来自其他语言。)
sub foo ( --> Int:D ) { Bool.pick ?? 1 !! fail 'oops' }
my $foo = foo; # $foo contains the failure object
Run Code Online (Sandbox Code Playgroud)
Nil实际上是一种通用的软故障。当分配给变量时,它只是将其重置为默认值。
my Int $foo = 1;
$foo = Nil;
say $foo.perl; # Int
Run Code Online (Sandbox Code Playgroud)
my Int:D $bar is default(42) = 1;
$bar = Nil
say $bar.perl; # 42
Run Code Online (Sandbox Code Playgroud)
典型的默认值与类型相同。
my Int $foo;
say $foo.VAR.default.perl; # Int
Run Code Online (Sandbox Code Playgroud)
一个特定的软故障将是返回一个类型对象
sub foo ( --> Int ){
Bool.pick ?? 1 !! Int
}
Run Code Online (Sandbox Code Playgroud)
这就是为什么我说的Nil是“ 通用 ”软故障。
通常,如果要定义变量的类型,则希望它具有该类型。因此,如果代码中包含其他类型的内容,则应立即进行投诉。
有更好的方法来处理Failure。
sub foo(--> Int:D ) { rand < 0.5 ?? 1 !! fail 'oops' }
with foo() -> Int:D $foo {
… # use $foo here
} else -> $fail {
… # use $fail here
}
Run Code Online (Sandbox Code Playgroud)
之所以Failure可行,是因为始终将自己视为未定义。
您也可以将其与 when
given foo() {
when Int:D -> Int:D $foo {
… # use $foo here
}
when Failure:D -> Failure:D $fail {
# a DEFINITE Failure value
# (`DEFINITE` is different than `defined`.)
}
default {
… # handle unexpected values (can be removed if not needed)
}
}
Run Code Online (Sandbox Code Playgroud)
或者,//如果您不在乎它是哪种类型的失败,则只需定义或运算符。
my Int:D $foo = foo() // 1;
Run Code Online (Sandbox Code Playgroud)
您甚至可能希望使用它来将a Failure转换为Nil。
my Int:D $foo is default(42) = foo() // Nil;
Run Code Online (Sandbox Code Playgroud)
如果您真的想要一个可能失败的子集,我认为这应该可行:
sub Maybe-Failure ( Any:U ::Type ) {
anon subset :: of Any where Type | Failure
}
my constant Maybe-Int = Maybe-Failure(Int);
# note that the type has to be known at compile-time for this line:
my Maybe-Int $foo = foo;
Run Code Online (Sandbox Code Playgroud)
不过目前无法使用。
(请注意,Mu除非您需要专门处理类型之外的类型和值,否则您不应该进行处理Any;例如Junction和IterationEnd。)
还有一些可能也应该起作用的事情是:
my class Maybe-Failure {
method ^parameterize ( $, Any:U ::Type ) {
anon subset :: of Any where Type | Failure
}
}
my Maybe-Failure[Int] $foo;
Run Code Online (Sandbox Code Playgroud)
这似乎由于与另一个原因相同的原因而失败。
另一种方法是创建一种新的类,例如subset。
那就是subset使用与Perl6中其余类不同的MOP。
TL; DR有关解决方案,请参见@Kaiepi自己的答案。但是P6中的每个非本机类型都已经自动成为一种增强的可为空类型,类似于增强的Maybe类型。因此,这也需要讨论。为了帮助构造我的答案,我将假装这是一个XY问题,即使并非如此。
我想定义一个
Maybe subset用于此
参见@Kaiepi的答案。
该subset解决方案是大材小用什么维基百科定义为Maybe类型,其归结为:
None[或]原始数据类型
事实证明,所有非本机P6类型都已经类似于增强的Maybe类型。
增强之处在于(相当于P6)None知道它与之配对的原始数据类型:
my Int $foo;
say $foo # (Int) -- akin to an (Int) None
Run Code Online (Sandbox Code Playgroud)
我有很多可能会失败的函数,但是在其签名中定义了一个返回类型。
如您所知,除非use fatal;有效,否则P6会故意让例程返回失败,即使存在不明确允许它们的返回类型检查。(subset返回类型检查可以显式拒绝它们。)
因此,考虑到返回类型检查Foo会自动转换为类似于subset带有where Failure | Foo子句的a ,可以理解的是,您认为可以通过创建匹配的子集来适应这种情况,以便在分配给变量时可以接受结果。
但是,从前面的讨论中可以清楚地看到,最好使用类似于Maybe类型的P6类型系统的内置方面。
一个Nil可以被用来指示什么叫做良性失败。因此,以下工作可指示失败(如您希望在某些例程中所做的那样),并将接收变量设置为None(或等同于增强的P6):
sub foo (--> Int) { Nil }
my Int $bar = foo;
say $bar; # (Int)
Run Code Online (Sandbox Code Playgroud)
所以,一个选择是,你替换调用fail与return Nil(或只Nil)。
可以想象一种将所有s 降级为良性失败s的杂注(例如failsoft):FailureNil
use failsoft;
sub foo (--> Int) { fail }
my Int $bar = foo;
say $bar; # (Int)
Run Code Online (Sandbox Code Playgroud)
关于Maybe类型的Wikipedia简介还说:
一个独特但相关的概念...被称为可为空的类型(通常表示为
A?)。
与Int?某些语言用来表示可Int为空的语法所使用的最接近的P6 简单地是Int,没有问号。以下是有效的类型约束:
Int-P6等同于可为空Int或Maybe Int
Int:D-P6等同于不可为空Int或Just Int
Int:U-P6等于Intnull或(Int) None
(:D并且:U有明显的原因被称为类型笑脸。:))
在静态类型的语言中,可为空的类型是[Maybe]类型(按功能编程术语而言),而在动态类型的语言(其中值具有类型,而变量没有),则通过具有单个null值来提供等效的行为。。
在P6中:
值有类型-变量也有类型。
P6类型类似于增强Maybe型(如上所述)或增强的可空类型,其中Nones或“ null”值与类型一样多,而不仅仅是一个None或空值。
(因此,P6是静态类型的语言还是动态类型的语言?它实际上超越了静态与动态,而是静态与动态。)
继续:
基本类型(例如整数和布尔值)通常不能为null,但是相应的可为null的类型(分别为可为null的整数和可为null的布尔值)也可以采用该
NULL值。
在P6中,所有非本机类型(如任意精度Int类型)都类似于增强的Maybe / nullable类型。
相反,所有本机类型(例如int-所有小写字母)都是非空类型-维基百科称之为原始类型。它们不能为null或None:
my int $foo;
say $foo; # 0
$foo = int; # Cannot unbox a type object (int) to int
Run Code Online (Sandbox Code Playgroud)
最后,返回维基百科Maybe页面:
[也许]类型和可空类型之间的核心区别在于[也许]类型支持嵌套
(Maybe (Maybe A) ? Maybe A),而可空类型不支持嵌套(A?? = A?)。
P6的内置类型在不使用子集的情况下不支持这种嵌套。因此,P6类型类似于增强Maybe类型,实际上只是增强的可为空类型。
布拉德·吉尔伯特的答案为我指明了正确的方向,尤其是:
另一种方法是创建一种新的类,例如子集。那就是子集使用与Perl6中其余类不同的MOP。
我想出的解决方案是这样的:
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
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
283 次 |
| 最近记录: |