Mixing Private and Public Attributes and Accessors in Raku

#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)
so far, so reasonable, and then
#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

I like the immediacy of the ‘=‘ assignment, but I need the ease of bunging in side actions that multi methods provide. I understand that these are two different worlds, and that they do not mix.

BUT - I do not understand why I can’t just go $c.v( 43 ) To set a public attribute

  1. I feel that raku is guiding me to not mix these two modes - some attributes private and some public and that the pressure is towards the method method (with some : sugar from the colon) - is this the intent of Raku's design?
  2. Am I missing something?

Raku Objects: Confusing or What? – Physics::Journey, Chapter 1: The Convenience Seeker Coming from Python, the Raku object / mixing-private-and-public-attributes-and-accessors-in-raku). From 2020.02, you can use the `is built` attribute on private attributes: `has $!x is built`. That means it *will* allow it to be set from `.new` as a named parameter, but that it will *not* create an accessor for it. This allows you to get rid of the `BUILD` method. Like Liked by 2 people

BUT - I do not understand why I can’t just go $c.v( 43 ) To set a public attribute

Well, that's really up to the architect. But seriously, no, that's simply not the standard way Raku works.

Now, it would be entirely possible to create an Attribute trait in module space, something like is settable, that would create an alternate accessor method that would accept a single value to set the value. The problem with doing this in core is, is that I think there are basically 2 camps in the world about the return value of such a mutator: would it return the new value, or the old value?

Please contact me if you're interested in implementing such a trait in module space.

Raku Objects: Confusing or What? : rakulang, misunderstood), my answer to your recent SO question "Mixing Private and Public Attributes and Accessors in Raku" includes the comment:. Mixing Private and Public Attributes and Accessors by p6steve. Using any or none on the hash keys and values by Lars Malmsteen. Import raku’s dir function into Perl by con. How to operator join two matrix by chia yi hung. Use of uninitialized value of type Any in numeric context by Lars Malmsteen. How to modify matrix by chia yi hung.

I currently suspect you just got confused.1 Before I touch on that, let's start over with what you're not confused about:

I like the immediacy of the = assignment, but I need the ease of bunging in side actions that multi methods provide. ... I do not understand why I can’t just go $c.v( 43 ) To set a public attribute

You can do all of these things. That is to say you use = assignment, and multi methods, and "just go $c.v( 43 )", all at the same time if you want to:

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
A possible source of confusion1

Behind the scenes, has $.foo is rw generates an attribute and a single method along the lines of:

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

The above isn't quite right though. Given the behavior we're seeing, the compiler's autogenerated foo method is somehow being declared in such a way that any new method of the same name silently shadows it.2

So if you want one or more custom methods with the same name as an attribute you must manually replicate the automatically generated method if you wish to retain the behavior it would normally be responsible for.

Footnotes

1 See jnthn's answer for a clear, thorough, authoritative accounting of Raku's opinion about private vs public getters/setters and what it does behind the scenes when you declare public getters/setters (i.e. write has $.foo).

2 If an autogenerated accessor method for an attribute was declared only, then Raku would, I presume, throw an exception if a method with the same name was declared. If it were declared multi, then it should not be shadowed if the new method was also declared multi, and should throw an exception if not. So the autogenerated accessor is being declared with neither only nor multi but instead in some way that allows silent shadowing.

Object orientation, In Raku, all attributes are private, which means they can be accessed directly only thing as a public (or even protected) attribute, there is a way to have accessor Roles are mixed in using the does keyword preceding the name of the role� The JSON created from an instance should round trip to a new instance with the same values for the "public attributes". "Private" attributes (that is ones without accessors,) will be ignored for both serialisation and de-serialisation.

Type system, They do not have public accessor methods generated automatically. It is meant to set private and public attributes of a class and receives all names attributes Roles can be mixed into classes and objects at runtime and compile time. Class attributes may also be declared with a secondary sigil – in a similar manner to instance attributes – that will generate read-only accessors if the attribute is to be public. Methods. While attributes give objects state, methods give objects behaviors. Let's ignore the new method temporarily; it's a special type of method.

Class, Role And Attribute Accessor in Raku, Class, Role And Attribute Accessor in Raku like to explain that a public attribute gets an automatic accessor method. In our case it is sufficient to break role application into parts and mix them up with other steps properly. Public/Private. For all the trait parameters, if it is applied to a private attribute then all auto-generated methods will be private too. The call-back style options such as builder, trigger, filter are expected to share the privace mode of their respective attribute:

M�lange d'attributs et d'accesseurs priv�s et publics dans Raku, #Private attribute example class C { has $!w; #private attribute multi method w is rw #public attribute with automatic accessors } my $c = C.new $c.v = 42 say� Attributes are variables that exist per instance of a class; when instantiated to a value, the association between the variable and its value is called a property. They are where the state of an object is stored. In Raku, all attributes are private, which means they can be

Comments
  • You could use a Proxy
  • How could you use a Proxy? I mean, the accessor already returns a container when is rw is specified. Returning a Proxy is not going to change the number of allowable parameters on the accessor.
  • @ElizabethMattijsen Perhaps I misunderstood the question. But this seems to work for what he wants (enabling both = foo and .(foo) for setting) and enabling side effects to be done in both cases (but not when only fetched): tio.run/…
  • Good point on cautioning the Proxys (even though I suggested it ha). The only time I've found them terribly useful is for my LanguageTag. Internally, the $tag.region returns an object of type Region (as it's stored internally), but the reality is, it's infinitely more convenient for people to say $tag.region = "JP" over $tag.region.code = "JP". And that's really only temporary until I can express a coercion from Str in the type, e.g., has Region(Str) $.region is rw (which requieres two separate planned but low priority features)
  • Thank you @Jonathan for taking time to elaborate the design rationale - I had suspected some kind of oil and water aspect and for a non CS bod like me, I really get the distinction between proper OO with hidden state and the application of class es as a friendlier way to build esoteric data holders which would take a PhD in C++. And to user072... for your thoughts on Proxy s. I did know about Proxy s before but suspected that they (and/or traits) are deliberately quite onerous syntax to discourage the mixing of oil and water...
  • p6steve: Raku was really designed to make the most common/easy things super easy. When you deviate from the common models, it's always possible (I think you've seen about three different ways to do what you want so far...and there's definitely more), but it makes you work a bit — just enough to make sure what you're doing is really want you want. But via traits, proxies, slangs, etc, you can make it so you only need a few extra characters to really enable some cool stuff when you need it.
  • Thanks @Elizabeth - this is an interesting angle - I am only hitting this question here and there and there is not enough payback in building a trait to do the trick (or skill on my part). I was really trying to get my head around the best coding practice and align to that - and hoping that Raku's design would be aligned to best practice(s), which I gather it is.
  • Aha - thanks @raiph - that's the thing I felt was missing. Now it makes sense. Per Jnthn I will probably try to be a better true OO coder and keep the setter style for pure data containers. But it's good to know that this is in the toolbox!