【问题标题】:Creating a Maybe type in Perl 6在 Perl 6 中创建 Maybe 类型
【发布时间】:2019-07-31 00:26:11
【问题描述】:

我有很多可能会失败的函数,但它们的签名中也定义了返回类型。由于我喜欢尽可能定义变量的类型,因此我想定义一个 Maybe 子集来用于此目的。我想出的是这样的:

subset Maybe is export of Mu where Mu | Failure;

问题在于FailureMu 的子类,所以这将匹配任何东西,而我真正想要的是能够动态匹配一个特定类型和Failure。我的下一个想法是创建一个参数化角色以用作类型,因为我不想为每个也可能是Failure 的单一类型创建子集。我想它看起来像这样:

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

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

my Maybe[Int] $foo = foo;

只有我不知道我需要为角色添加什么才能完成这项工作。是否可以创建这样的角色?如果没有,是否有另一种方法可以创建一个类型来做我想做的事?

【问题讨论】:

    标签: types raku


    【解决方案1】:

    Perl6 类型已经是 Maybe 类型。

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


    这是Maybe[Int] 变量:

    my Int $a;
    my Int:_ $a; # more explicit
    

    这有一个明确的Int

    my Int:D $a = …; # must be assigned because the default is not “definite”
    

    这包含一个空的Int

    my Int:U $a;
    

    请注意FailureNil 的子类型,因此即使指定了返回类型的子例程也可以返回它们。
    Nil 不像其他语言的 nullnil。)

    sub foo ( --> Int:D ) { Bool.pick ?? 1 !! fail 'oops' }
    
    my $foo = foo; # $foo contains the failure object
    

    Nil 实际上是一种通用软故障。当分配给一个变量时,它只是将其重置为默认值。

    my Int $foo = 1;
    
    $foo = Nil;
    
    say $foo.perl; # Int
    
    my Int:D $bar is default(42) = 1;
    
    $bar = Nil
    
    say $bar.perl; # 42
    

    典型的默认与类型相同。

    my Int $foo;
    
    say $foo.VAR.default.perl; # Int
    

    一个特定的软故障是返回一个类型对象

    sub foo ( --> Int ){
      Bool.pick ?? 1 !! Int
    }
    

    这就是为什么我说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
    }
    

    这是可行的,因为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)
      }
    }
    

    或者只是定义或运算符//,如果你不在乎它是什么类型的失败。

    my Int:D $foo = foo() // 1;
    

    您甚至可能想用它来将Failure 变成Nil

    my Int:D $foo is default(42) = foo() // Nil;
    

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

    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;
    

    它目前不起作用。

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

    其他可能也应该起作用的是:

    my class Maybe-Failure {
      method ^parameterize ( $, Any:U ::Type ) {
        anon subset :: of Any where Type | Failure
      }
    }
    
    my Maybe-Failure[Int] $foo;
    

    这似乎与另一个失败的原因相同。


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

    【讨论】:

    • 这是对处理失败的不同方法的一个很好的解释,但我正在寻找一种方法来定义一个可能是 Failure 的类型,我可以在分配返回值时使用它在正确处理变量之前可能会失败的函数。这样,我的代码可能会更简洁一些
    【解决方案2】:

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

    解决 Y

    我想定义一个Maybe subset 用于此

    查看@Kaiepi 的回答。

    所有非原生 P6 类型都已经类似于 Maybe 类型

    subset 解决方案对于 wikipedia 定义为 Maybe 类型的东西来说太过分了,归结为:

    None[或]原始数据类型

    事实证明,所有非原生 P6 类型已经类似于增强的 Maybe 类型。

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

    my Int $foo;
    say $foo        # (Int) -- akin to an (Int) None
    

    求解 X

    我有很多可能会失败的函数,但它们的签名中也定义了返回类型。

    您大概知道,除非use fatal; 生效,否则P6 会故意允许例程返回失败,即使存在未明确允许的返回类型检查。 (subset 返回类型检查可以明确拒绝它们。)

    因此,鉴于返回类型检查 Foo 会自动转换为类似于带有 where Failure | Foo 子句的 subset 的内容,因此您认为通过创建匹配子集来适应这种情况是可以理解的,因此您可以接受结果分配给变量时。

    但希望从前面的讨论中可以清楚地看出,使用 P6 类型系统的内置方面可能会更好,它类似于 Maybe 类型。

    Nil 可用于指示所谓的良性 故障。因此,以下工作表明失败(正如您希望在某些例程中执行的那样)将接收变量设置为 None(或者更确切地说是增强的 P6 等效项):

    sub foo (--> Int) { Nil }
    my Int $bar = foo;
    say $bar; # (Int)
    

    因此,一种选择是将对fail 的调用替换为return Nil(或只是Nil)。

    可以想象一个pragma(例如,称为failsoft)将所有Failures 降级为良性故障Nils:

    use failsoft;
    sub foo (--> Int) { fail }
    my Int $bar = foo;
    say $bar; # (Int)
    

    可为空的类型

    关于 Maybe 类型的维基百科介绍也说:

    一个独特但相关的概念......称为可空类型(通常表示为A?)。

    与某些语言用来表达可空IntInt? 语法最接近的P6 就是Int,不带问号。以下是有效的类型约束:

    • Int -- P6 等效于可空的IntMaybe Int

    • Int:D -- P6 等效于不可为空的 IntJust Int

    • Int:U -- P6 等效于 Int null 或 (Int) None

    :D and :U 被称为类型表情符号的原因很明显。:))

    继续,wikipedia's Nullable types page 说:

    在静态类型语言中,可为空的类型是 [a Maybe] 类型(在函数式编程术语中),而在动态类型语言中(其中 值有类型,但变量有不是),通过具有单个空值来提供等效的行为。

    在 P6 中:

    • 值有类型——但变量也有。

    • P6 类型类似于增强的Maybe 类型(如上所述)或增强的可空类型,其中Nones 或“null”值与类型一样多,而不仅仅是一个单个None 或空值。

    (那么,P6 是静态类型语言还是动态类型语言?实际上是Beyond static vs dynamic,而是静态动态。)

    继续:

    整数和布尔值等原始类型通常不能为空,但相应的可空类型(分别为可空整数和可空布尔值)也可以采用NULL 值。

    在 P6 中,所有非本机类型(如任意精度 Int 类型)都类似于增强的 Maybe/nullable 类型。

    相比之下,所有native types(如int——全小写)都是不可为空的类型——维基百科称之为原始类型。它们不能为 null 或 None:

    my int $foo;
    say $foo;    # 0
    $foo = int;  # Cannot unbox a type object (int) to int
    

    最后,回到维基百科Maybe页面:

    [maybe] 类型和可空类型之间的核心区别在于 [maybe] 类型支持嵌套(Maybe (Maybe A) ≠ Maybe A),而可空类型不支持(A?? = A?)

    P6 的内置类型不支持在不使用子集的情况下以这种方式嵌套。所以 P6 类型,虽然 类似于 到增强的 Maybe 类型,但实际上只是增强的可空类型。

    【讨论】:

    • 关于我所说的 Maybe 实际上并不是 Maybe 的定义好点
    • 我认为现在的解释更好。在解决 X 部分中,虽然我可以看到在某些情况下返回 Nil 会比失败更好,但当我问这个问题时,我正在写它没有。当出现问题时,我使用失败消息通知程序用户,对于某些例程,我让他们的异常在某些地方抛出异常以其他方式处理它们。
    【解决方案3】:

    Brad Gilbert's answer 为我指明了正确的方向,尤其是:

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

    我想出的解决方案是这样的:

    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
    

    【讨论】:

      猜你喜欢
      • 2018-10-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多