Смешивание личных и общедоступных c атрибутов и аксессоров в Raku - PullRequest
12 голосов
/ 09 января 2020
#Private attribute example
class C { 
    has $!w;                            #private attribute
    multi method w { $!w }              #getter method
    multi method w ( $_ ) {                 #setter method
        warn “Don’t go changing my w!”;   #some side action
        $!w = $_
    }  
}
my $c = C.new
$c.w( 42 )
say $c.w #prints 42
$c.w: 43
say $c.w #prints 43

#but not
$c.w = 44
Cannot modify an immutable Int (43)

пока, настолько разумно, а затем

#Public attribute example
class C { 
    has $.v is rw    #public attribute with automatic accessors
}
my $c = C.new
$c.v = 42
say $c.v #prints 42

#but not
$c.v( 43 ) #or $c.v: 43
Too many positionals passed; expected 1 argument but got 2

Мне нравится непосредственность назначения '=', но мне нужна легкость вставки в побочные действия, которые обеспечивают несколько методов. Я понимаю, что это два разных мира, и что они не смешиваются.

НО - Я не понимаю, почему я не могу просто go $ c .v (43). c атрибут

  1. Я чувствую, что Раку подсказывает мне не смешивать эти два режима - некоторые атрибуты приватны, а некоторые публикуются c и что давление на метод метода (с некоторыми: сахар) от двоеточия) - это замысел Раку?
  2. Я что-то упустил?

Ответы [ 3 ]

13 голосов
/ 10 января 2020

В этом ли смысл замысла Раку?

Справедливо сказать, что Раку не совсем не придуман в этой области. Ваш вопрос затрагивает две темы в дизайне Raku, которые стоит немного обсудить.

Raku имеет первоклассные l-значения

Raku широко использует l-значения, будучи первым классная вещь. Когда мы пишем:

has $.x is rw;

Генерируемый метод:

method x() is rw { $!x }

Здесь is rw указывает, что метод возвращает l-значение это то, что может быть назначено. Таким образом, когда мы пишем:

$obj.x = 42;

Это не syntacti c sugar: это действительно вызов метода, а затем к его результату применяется оператор присваивания. Это работает, потому что вызов метода возвращает контейнер атрибута Scalar, который затем может быть назначен. Можно использовать связывание, чтобы разделить это на два шага, чтобы увидеть, что это не тривиальное преобразование syntacti c. Например, это:

my $target := $obj.x;
$target = 42;

будет присваиваться атрибуту объекта. Этот же механизм стоит за многими другими функциями, включая назначение списка. Например, это:

($x, $y) = "foo", "bar";

Работает путем построения List, содержащего контейнеры $x и $y, и затем оператор присваивания в этом случае выполняет итерацию каждой стороны попарно для выполнения присваивания. Это означает, что мы можем использовать rw объектные средства доступа:

($obj.x, $obj.y) = "foo", "bar";

И все это просто естественно работает. Это также механизм присвоения срезов массивов и хэшей.

Можно также использовать Proxy, чтобы создать контейнер с l-значением, в котором его поведение читается и записывается. находятся под вашим контролем. Таким образом, вы можете поместить побочные действия в STORE. Однако ...

Раку поощряет методы semanti c вместо "сеттеров"

Когда мы описываем ОО, часто встречаются такие термины, как "инкапсуляция" и "сокрытие данных". Ключевая идея здесь заключается в том, что модель состояния внутри объекта - то есть способ, которым он выбирает для представления данных, которые ему необходимы для реализации его поведения (методов) - может свободно развиваться, например, для обработки новых требований. Чем сложнее объект, тем более освобождающим становится этот объект.

Однако методы получения и установки - это методы, которые имеют неявную связь с состоянием. Хотя мы можем утверждать, что достигаем сокрытия данных, потому что мы вызываем метод, а не обращаемся непосредственно к состоянию, мой опыт показывает, что мы быстро оказываемся в месте, где внешний код выполняет последовательности вызовов установщика для выполнения операции, которая форма особенность зависти анти-шаблон. И если мы делаем , что , то вполне вероятно, что в итоге мы получим logi c вне объекта, который выполняет сочетание операций получения и установки для выполнения операции. Действительно, эти операции должны были быть представлены как методы с именами, которые описывают то, что достигается. Это становится еще более важным, если мы находимся в параллельной обстановке; Хорошо спроектированный объект часто довольно легко защитить на границе метода.

Тем не менее, многие применения class действительно являются типами записей / продуктов: они существуют для простой группировки нескольких элементов данных. Не случайно, что символ . не только генерирует метод доступа, но также:

  • Определяет атрибут для установки по умолчанию при инициализации объекта logi c (то есть, class Point { has $.x; has $.y; } может быть создан как Point.new(x => 1, y => 2)), а также отображается в методе .raku.
  • Определяет атрибут в объекте .Capture по умолчанию, что означает, что мы можем использовать его при деструктурировании (например, sub translated(Point (:$x, :$y)) { ... }).

Что бы вы хотели, если бы писали в более процедурном или функциональном стиле и использовали class в качестве средства для определения типа записи.

Дизайн Raku не оптимизирован для умных действий в сеттерах, потому что это плохо для оптимизации. Это выше того, что нужно для типа записи; в некоторых языках мы можем утверждать, что хотим сделать проверку того, что присваивается, но в Raku мы можем обратиться к subset типам для этого. В то же время, если мы действительно занимаемся проектированием ОО, нам нужен API осмысленного поведения, который скрывает модель состояния, а не думать с точки зрения геттеров / сеттеров, что, как правило, приводит к невозможности совместного размещения данные и поведение, что в любом случае является основной целью ОО.

7 голосов
/ 10 января 2020

НО - я не понимаю, почему я не могу просто go $ c .v (43) Установить атрибут publi c

Ну вот и все действительно до архитектора. А если серьезно, нет, это просто не стандартный способ работы Raku.

Теперь вполне возможно создать черту Attribute в модульном пространстве, что-то вроде is settable, которая создаст альтернативный метод доступа метод, который будет принимать одно значение для установки значения. Проблема с этим в ядре состоит в том, что я думаю, что в мире существует 2 лагеря с возвращаемым значением такого мутатора: вернет ли он значение new или old значение?

Пожалуйста, свяжитесь со мной, если вы заинтересованы в реализации такой черты в модульном пространстве.

6 голосов
/ 10 января 2020

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

Мне нравится непосредственность назначение =, но мне нужна легкость вставки в побочные действия, которые предоставляют несколько методов. ... Я не понимаю, почему я не могу просто go $c.v( 43 ) установить атрибут publi c

Вы можете сделать все эти вещи. То есть вы используете = присваивание, и несколько методов, и «просто go $c.v( 43 )», все одновременно, если вы хотите:

class C {
  has $!v;
  multi method v                is rw {                  $!v }
  multi method v ( :$trace! )   is rw { say 'trace';     $!v }
  multi method v ( $new-value )       { say 'new-value'; $!v = $new-value }
}
my $c = C.new;
$c.v = 41;
say $c.v;            # 41
$c.v(:trace) = 42;   # trace
say $c.v;            # 42
$c.v(43);            # new-value
say $c.v;            # 43

Возможный источник путаницы 1

За кулисами has $.foo is rw генерирует атрибут и один метод в соответствии с:

has $!foo;
method foo () is rw { $!foo }

Вышеприведенное не совсем верно. Учитывая поведение, которое мы наблюдаем, автоматически сгенерированный метод foo компилятора так или иначе объявляется таким образом, что любой новый метод с тем же именем без вывода сообщений shadows it. 2

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

Сноски

1 См. ответ jnthn для четкого, полного и авторитетного изложения мнения Раку о частном и публичном c добытчиках / сеттеры и что он делает за кулисами, когда вы объявляете publi c getters / setters (т.е. пишите has $.foo).

2 Если был объявлен автоматически созданный метод доступа для атрибута only, тогда Раку, я полагаю, выдаст исключение, если будет объявлен метод с тем же именем. Если он был объявлен multi, то он не должен скрываться, если новый метод был также объявлен multi, и должен выдавать исключение, если нет. Таким образом, автоматически сгенерированный метод доступа объявляется ни с only, ни с multi, но вместо этого каким-то образом, позволяющим скрытое теневое копирование.

...