【问题标题】:How do I declare a class attribute as a union of class names?如何将类属性声明为类名的联合?
【发布时间】:2018-09-14 13:44:48
【问题描述】:

我正在阅读一个寻找不同结构的电子表格。当我使用 Moose 尝试以下操作时,它似乎做了我想做的事。我可以创建不同类型的对象,将其分配给找到的成员 并转储 Cell 实例以供审核。

package Cell
{
  use Moose;
  use Moose::Util::TypeConstraints;
  use namespace::autoclean;

  has 'str_val'    => ( is => 'ro', isa => 'Str', required => 1 );
  has 'x_id'       => ( is => 'ro', isa => 'Str', ); # later required => 1 );
  has 'color'      => ( is => 'ro', isa => 'Str', );
  has 'border'     => ( is => 'ro', isa => 'Str', );
  has 'found'      => ( is => 'rw', isa => 'Sch_Symbol|Chip_Symbol|Net', );
  1;
}

如果我尝试在 Perl 6 中做同样的事情,它将无法编译。

class Cell {
  has Str $.str_val              is required;
  has Str $.x_id                 is required;
  has Str $.color;
  has Str $.border;
  has Sch_Symbol|Chip_Symbol|Net $.found is rw
}
Malformed has
at C:\Users\Johan\Documents/moose_sch_3.pl6:38
------>   has Sch_Symbol'<'HERE'>'|Chip_Symbol|Net $.found is rw

如何在 Perl 6 中做到这一点?

【问题讨论】:

    标签: raku


    【解决方案1】:

    您可能希望他们扮演一个共同的角色并将其指定为类型

    role Common {}
    class Sch-Symbol does Common {…}
    …
    
    class Cell {
      …
      has Common $.found is rw;
    }
    

    否则您将不得不使用where 约束

    class Cell {
      …
      has $.found is rw where Sch-Symbol|Chip-Symbol|Net;
    }
    

    您还可以创建一个子集来包装where 约束。

    subset Common of Any where Sch-Symbol|Chip-Symbol|Net;
    
    class Cell {
      …
      has Common $.found is rw;
    }
    

    请注意,where 约束比使用普通角色要慢。

    【讨论】:

      【解决方案2】:

      这是 Brad Gilbert 在his answer 中提到的subset/where 解决方案的完整实现。这包括Common 中每个类的一个示例,以及一个显示不满足类型约束时会发生什么的示例:

      #!/bin/env perl6
      
      class  Sch-Symbol { has Str $.name }
      class Chip-Symbol { has Num $.num  }
      class         Net { has Int $.id   }
      
      subset Common of Any where Sch-Symbol|Chip-Symbol|Net;
      
      class Cell {
        has Str $.str_val  is required;
        has Str $.x_id     is required;
        has Str $.color;
        has Str $.border;
        has Common $.found is rw;
      }
      
      my $str_val = 'foo';
      my $x_id    = 'bar';
      
      my @founds = (
          Net.new(:42id),                 # will work
          Sch-Symbol.new(:name<baz>),     # will work
          Chip-Symbol.new(num => 1E101),  # will work
          42,                             # won't work
      );
      
      for @founds -> $found {
         my $cell =  Cell.new(:$str_val, :$x_id, :$found);
         dd $cell;
      }
      

      假设这是在文件test.p6 中,当我们运行perl6 test.p6 时,我们得到:

      Cell $cell = Cell.new(str_val => "foo", x_id => "bar", color => Str, border => Str, found => Net.new(id => 42))
      Cell $cell = Cell.new(str_val => "foo", x_id => "bar", color => Str, border => Str, found => Sch-Symbol.new(name => "baz"))
      Cell $cell = Cell.new(str_val => "foo", x_id => "bar", color => Str, border => Str, found => Chip-Symbol.new(num => 1e+101))
      Type check failed in assignment to $!found; expected Common but got Int (42)
        in submethod BUILDALL at test.p6 line 9
        in block <unit> at test.p6 line 28
      

      【讨论】:

        【解决方案3】:

        你可以使用where

        has $.found is rw where Sch_Symbol|Chip_Symbol|Net;
        

        或通过subset定义新类型

        subset Stuff where Sch_Symbol|Chip_Symbol|Net;
        
        class Cell {
            has Str   $.str_val is required;
            has Str   $.x_id    is required;
            has Str   $.color;
            has Str   $.border;
            has Stuff $.found   is rw;
        }
        

        【讨论】:

        • 正如布拉德所说,一个共同的角色总是更快。由于其不同的关注点分离,它通常也比subset/where 更合适。如果你声明了Common 角色,那么你所要做的就是用does Common 声明你的类。或者甚至跳过它,只需将but Common 附加到需要通过Common 类型检查的对象。相比之下,subset/where 必须针对您想要包含的每个类进行修改。如果它是 use 模块中的 subset,则需要旧模块的 new 子集(仍然如此缓慢),并且必须将约束切换为新名称。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-05-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-01-02
        • 2019-11-26
        • 1970-01-01
        相关资源
        最近更新 更多