Perl6 — Классы

в 18:50, , рубрики: method, perl, perl6, классы, Программирование, метки: , ,

1. Особенности работы с переменными и литералами в Perl6
2. Perl6 — Операции над переменными, анонимные блоки
3. Perl6 — Условные операторы, циклы
4. Perl6 — Работа с функциями

Последние несколько дней я читал раздел по работе с объектами в Perl6. К сожалению я так и не смог понять некоторые моменты, и надеюсь что те кто уже смог их понять подскажут, в чем же дело. Как и раньше, все описанные возможности будут сопровождаться небольшими экспериментами или примерами, чтобы пояснить как это работает, или как нужно использовать.
Приступим:

В Perl6 есть два возможных способа объявить класс:
-сделать весь файл описанием класса

class Name;
has $.field;
...
method Func() {...};

-объявить класс с помощью конструкции

class Name
 {
     has $.field;
 }

Можно как и в языке C++ указать лишь имя класса, дав его описание позднее:

class Name {...};
my $var = Name.new;
class Name {has $.elem};

Если последней строчки не будет то произойдет ошибка при компиляции
Так же есть возможность сделать следующее:

my $className = class Name
 {
     has $.field is rw;
 }
my $var1 = Name.new;
my $var2 = $className.new;
$var1.field = 10;
$var2.field = 5;
say $var1.field, ' ', $var2.field;

В результате увидим строку '10 5'
Можно создать анонимный класс с помощью конструкции

my $anonClass = class :: {has $.field};
my $var = $anonClass.new;

Уже созданные переменные могут быть использованы для создания новых переменных:

my $var1 = ClassName.new;
my $var2 = $var1.new;

Однако создание происходит без копирования данных.

Теперь подробнее о том что можно указывать при объявлении:
для указания поля используется ключевое слово has:

class Name
 {
     has Int $.publicMember;
     has $!privateMember;
     has @.publicArray is rw;
     has %!privateHash;
     has $.a is rw;
     has $.b is rw = 'Значение по умолчанию';
 }
my $v = Name.new;
say $v.publicMember;  #выведет "Int()"
#$v.publicMember = 10 - Ошибка, переменная readonly
#say $v!privateMember - Ошибка
$v.mas = 1, 2, 3;
#say $v.privateHash - Ошибка
$v.a = 'new value';
say $v.b  #Выведет "Значение по умолчанию"

Так же стоит отметить, что в теле метода можно изменять приватные переменные, даже если указано is readonly (Однако я не могу сказать, это так и задумано, или сказывается незаконченность вирт. машины)

Методы у классов указываются ключевым словом method:

class Name
 {
     has $!privateMember = 5;

     method GetPrivateMember()
      {
          return $!privateMember;
      }
      
     method SetPrivateMember($value)
      {
          $!privateMember = $value if $value > 0;
      }
 }
my $var = Name.new;
say $var.GetPrivateMember;
$var.SetPrivateMember(10);
$var.SetPrivateMember: 20;

Методы могут быть объявлены приватными:

method !PrivateMethod()
 {
     say 'Hello!';
 }

Можно указать через какую переменную будет доступен объект (инвокант), у которого вызывается метод:

method Func($self: $param1, $param2)
 {
     $self.Func2($param1);
 }

Имя переменной не обязательно должно быть $self

method Func($someName: $param1, $param2)
 {
     $someName!PrivateMethod($param1);
 }

Если эту переменную не указывать то сам объект будет доступен как 'self'

method Func($param1, $param2)
 {
     self.PublicMethod($param1);
     self!PrivateMethod($param2);
 }

Для вызова в теле метода другого метода этого класса используется следующая конструкция

self!PrivateMethod();
self.PublicMethod();

Например

class Name
 {
     method !A()
      {
          say "!A";
      }
     
     method A($s:)
      {
          $s!A();
      }
      
     method B()
      {
          self!A;
          self.C;
          say "B";
      }
      
     method C()
      {
          say "C";
      }
 }

При этом возможно делать вызовы self.A и self!A

Для доступа к полям класса в теле метода используется теже самые имена, что и при объявлении:

class Name
 {
     has $.a;
     has $!b;
     
     method PrintValues()
      {
          say $.a;
          say $!b;
      }
 }

Также можно написать self.a но нельзя написать self!b, т.к. в этом случае будет подразумеваться вызов приватного метода b, а не обращение к переменной b

Если имени класса при объявлении указать is rw то все поля, которые будут содержаться в этом классе станут доступны для записи без указания is rw для каждого поля в отдельности:

class Name is rw
 {
     has $.a;
     has $.b;
 }

Также в классах можно объявлять submethod'ы. Отличие их от обычных методов в том, что submethod наследоваться дочерним классом не будет.

Теперь о конструкторах
Можно указать какие действия выполнить при создании нового экземпляра класса:

class Name
 {
     has $.arg1 is rw = 10;
     has $!arg2;
     submethod BUILD(:$namedArg1, :$namedArg2)
      {
          self.arg1 = $namedArg1 if $namedArg1>0;
          $!arg2 = $namedArg2;
      }
 }
my $var = Name.new(:namedArg2(200), :namedArg1<Hello>);

Есть одна вещь которая мне непонятна: мне пришлось писать self.arg1 а не $.arg1, т.к. мне выдавалась ошибка «Virtual call $.a may not be used on partially constructed objects».
Я не могу понять, так и задумано или же это просто ещё не до конца проработанная часть. Но в любом случае, такой конструктор работает нормально.

Также можно указывать деструктор, который будет вызываться автоматически GarbageCollector'ом.

submethod DESTROY()
 {
     say 'DESTROY';
 }

Но на этот раз, вышеуказанной надписи при проведении экспериментов я так и не смог увидеть. Подозреваю что эта часть тоже ещё не доделана.

В Perl6 наследование организуется следующей конструкцией:

class Name {has $.a};
class Name2 is Name {has $.b};
my $var = Name2.new;
say $var;  #Name2.new(b => Any, a => Any)

Возможно множественное наследование, в результате которого может получиться что имена функций или полей в классах будут совпадать:

class Name
 {
     has $.a is rw = 10;
     has $.c is rw = 15;
     method A() {say "Name ", $.a;}
 }
class Name2
 {
     has $.b is rw = 20;
     has $.c is rw = 25;
     method A() {say "Name2 ", $.b;}
 }
class Name3 is Name is Name2
 {
     method A()
      {
          self.Name2::A();
          say $.c;
      }
 }
my $var = Name3.new;
$var.a = 100;
$var.b = 200;
$var.A;

В данном случае будут выведены на экран две строчки:

Name2 200
15

Если не переопределять функцию A в дочернем класе, то функция Name::A перекроет функцию Name2::A, также происходит с переменной $.c
Как я понимаю строка self.Name2::A(); выполняет метод Name2::A() для объекта self, однако все мои догадки, как это работает портит тот факт, что на имя self.Name2 выдается ошибка, а так же нельзя написать self.(&Name2::A) или что-то вроде этого. Буду рад если кто-нибудь из знающих людей подскажет.

Конечно описанные здесь возможности это лишь часть из того что можно делать с объектами (например я даже не упомянул о делегировании, ролях — аналогов интерфейсов в с++), но пока что я не хотел бы так сильно углубляться в эту тему, а продолжить изучение других возможностей Perl6. А пока что для начала освоения языка хватит и описанного.

Автор: WarFair

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js