Перегрузка операторов для класса

Допустим, у меня есть следующий класс:

class A {
    has $.val;

    method Str { $!val ~ 'µ'  }  
}

# Is this the right way of doing it?
multi infix:<~>(A:D $lhs, A:D $rhs) {
    ('(', $lhs.val, ',', $rhs.val, ')', 'µ').join;
}

Как бы я перегрузил оператор (например, +) для класса таким же образом, как Str в предыдущем классе?

Я предполагаю, что это работает только для методов, которые вызываются на объекте экземпляра и с использованием multi operator-type:<OP>(T $lhs, T $rhs) { } для операторов - правильный путь, но я Я не уверен.

Например, в Python, похоже, существует соответствие между специальными методами, названными в честь операторов (например, operator.__add__) и операторов (например, +). Кроме того, любая перегрузка оператора для пользовательского класса выполняется внутри класса.

Ответы

Ответ 1

В Perl 6 операторы считаются частью текущего языка. Все вещи, которые относятся к текущему языку, определены лексически (то есть my -scoped). Следовательно, multi sub - правильная вещь.

Если поместить этот код в модуль, вы, вероятно, также захотите пометить multi для оператора is export:

multi infix:<~>(A:D $lhs, A:D $rhs) is export {
    ('(', $lhs.val, ',', $rhs.val, ')', 'µ').join;
}

Так что он будет доступен для пользователей, которые use или import модуль (use на самом деле определено с точки зрения import, а import импорт символов в лексической области видимости).

Хотя есть некоторые операторы, которые по умолчанию делегируют методам (например, prefix:<+> вызывает Numeric), между ними нет отношения 1:1, и для большинства операторов их реализация находится непосредственно в sub оператора (или распространяется через много multi sub).

Кроме того, набор операторов открыт, поэтому нельзя ограничивать перегрузку существующих операторов, но также можно вводить новые. Это поощряется, когда новое значение для оператора явно не связано с нормальной семантикой используемого символа; например, перегрузка + для добавления матрицы была бы разумной, но для чего-то, что нельзя было бы рассматривать как дополнение, новый оператор был бы лучшим выбором.

Ответ 2

class A {
    has $.val;

    method Str { $!val ~ 'µ'  }
}

multi infix:<~>(A:D $lhs, A:D $rhs) {
    ('(', $lhs.val, ',', $rhs.val, ')', 'µ').join;
}

dd A.new(val => "A") ~ A.new(val  => "B"); # "(A,B)µ"

Так что да, это правильный путь. Если вы хотите переопределить +, тогда имя создаваемого подпункта - infix:<+>.

Вы также можете указать регистр для объектов типа с помощью :U "type smiley", например:

multi infix:<~>(A:U $lhs, A:U $rhs) {
    'µ'
}

Надеюсь, что это ответ на ваш вопрос.