in reply to Raku classes: syntax question

Hello 7stud,

I can’t comment on Learning Perl 6 as I haven’t read it, but here is my understanding of the example Point class. Consider the following code which creates a Point object and sets the y attribute:

my Point $p = Point.new; $p.gist.put; $p.set( y => 4 ); $p.gist.put; $p.y.put;

Output:

17:44 >raku 2091_SoPW.raku [0, 0] [0, 4] 4 18:00 >

First, the attributes $.x and $.y are declared with dots rather than exclamation points, meaning that although they are private attributes, Raku will generate read-only getter methods for them. So, in the example above, the method call $p.y returns the value of the attribute $.y.

To set the attributes, it is necessary to provide the class with an explicit set method.

method set( :$x = $.x, :$y = $.y )
  1. :$x and :$y declare named parameters x and y, allowing the set method to be called like this: $p.set( x => 3, y => 7 );.
  2. = $.x (or the equivalent = $!x) sets the default value for that parameter. So in the call $p.set( y => 4 ); the x attribute, not being named in the method call, receives its default value, namely $.x, ensuring that the assignment $!x = $x; leaves the x attribute unchanged.

Note: Raku allows you to sometimes use $.x as an alias for $!x, but I find that makes things unnecessarily confusing. I think of it this way: the attribute is $!x, but it is declared as $.x to get Raku to provide it with a read-only getter method. So I would prefer to declare the set method as method set( :$x = $!x, :$y = $!y ). But, of course, YMMV.

$!x = $x; $!y = $y;

These statements simply set the object attributes to the values specified as arguments to the set method (or to their default values, if no arguments are specified in the call).

Hope that helps,

Athanasius <°(((><contra mundum סתם עוד האקר של פרל,

Replies are listed 'Best First'.
Re^2: Raku classes: syntax question
by 7stud (Deacon) on Jan 31, 2024 at 16:52 UTC

    :$x and :$y declare named parameters

    I looked up "named parameters", and it turns out the syntax used for the parameters in the set method:

    method set( :$x = $.x, :$y = $.y )

    is the same syntax used for any function:

    Named parameters In contrast to positional parameters, named parameters are referred by their names. The following function takes two parameters called $from and $to: sub distance(:$from, :$to) { $from - $to } Now, to call the function, you need to name the arguments: say distance(from => 30, to => 10); # 20 It is an error to pass the arguments as if they were positional. For example, a call distance(30, 10) generates an error: ...

    I found some basic information on the syntax used in classes here:

    class Rectangle { has Int $.length = 1; has Int $.width = 1; method area(--> Int) { return $!length * $!width; } } my $r1 = Rectangle.new(length => 2, width => 3); say $r1.area();

    We define a new Rectangle class using the class keyword. It has two attributes, $!length and $!width introduced with the has keyword. Both default to 1. Read only accessor methods are automatically generated. It is rarely necessary to explicitly write a constructor. An automatically inherited default constructor called new will automatically initialize attributes from named parameters passed to the constructor.

    'Attributes" (called fields or instance storage in other languages) are never directly accessible from outside of the class (this is in contrast to many other languages). This encapsulation is one of the key principles of object oriented design. The ! twigil (Ed. twigil = secondary sigil) emphasizes that this attribute is private to the class. The attribute is encapsulated. Private attributes will not be set by the default constructor by default...

    And this:

    has Bool $.done;

    This scalar attribute (with the $ sigil) has a type of Bool. Instead of the ! twigil, the . twigil is used. While Raku does enforce encapsulation on attributes, it also saves you from writing accessor methods. Replacing the ! with a . both declares a private attribute and an accessor method named after the attribute. In this case, both the attribute $!done and the accessor method done are declared. It's as if you had written:

    has Bool $!done; method done() { return $!done }

    Note that this is not like declaring a public attribute, as some languages allow; you really get both a private attribute and a method, without having to write the method by hand. You are free instead to write your own accessor method, if at some future point you need to do something more complex than returning the value.

    So I would prefer to declare the set method as method set( :$x = $!x, :$y = $!y ). But, of course, YMMV.

    Yes, that makes more sense to me. The variable's name is $!x (where the ! indicates it is a private variable) and as far as I know you only write $.x when you declare the instance variable in order to generate a read accessor.

    Here is an example I played with:

    use v6; class Point { has $.x = 0; has $.y = 0; method gist { "[$.x, $.y]" } method set( :$joe = $!x, :$susan = $!y ) { $!x = $joe; $!y = $susan; } } my $point = Point.new(); say $point.x; # 0 say $point; # [0, 0] # $point.x = 3; Error! $point = Point.new(x => 3, y => 5); say $point; # [3,5] $point.set(joe => 10, susan => 20); say $point; # [10, 20] $point.set(joe => 100); say $point; # [100, 20]

    Thanks for taking the time to help me. I really appreciate it.

      That's a good question and a good answer. Raku (like perl) has some niceties as you dig in ... but the defaults are set at "easy"

      Consider this...

      use v6; class Point { has $.x is rw = 0; has $.y is rw = 0; method gist { "[$.x, $.y]" } method set( :$joe = $.x, :$susan = $.y ) { #method set( :$joe = $!x, :$susan = $!y ) { $.x = $joe; $.y = $susan; } } class Pixel is Point { has Int $.x is rw = 0; has Int $.y is rw = 0; has $.color = '#000000'; } my $point = Pixel.new(); #my $point = Point.new(); say $point.x; # 0 say $point; # [0, 0] # $point.x = 3; Error! $point = Pixel.new(x => 3, y => 5); #$point = Point.new(x => 3, y => 5); say $point; # [3,5] $point.set(joe => 10, susan => 20); say $point; # [10, 20] $point.set(joe => 100); say $point; # [100, 20]

      The $!x / $!y variant does not work because it directly refers to the PRIVATE ATTR of the Point class. But, in this case, we have used inheritance to override the Point x/y with Pixel x/y. So, when you need to reach for inheritance then the $.x / $.y variants help ... since they are the same as saying self.x and self.y - ie. they route via the settor / getter METHODS.

      We need the 'is rw' to ask raku to make a settor method so that $.x = $joe; $.y = $susan; can use the assignment operation to set attr values

        ...fwiw I would do this
        use v6; class Point { has $.x is rw = 0; has $.y is rw = 0; method gist { "[$.x, $.y]" } method set(:$x, :$y) { $.x = $x with $x; $.y = $y with $y; } } class Pixel is Point { has Int $.x is rw = 0; has Int $.y is rw = 0; has $.color = '#000000'; } my $point = Pixel.new(); say $point.x; # 0 say $point; # [0, 0] $point.x = 3; # Works now! $point = Pixel.new( :x(3), :y(5) ); say $point; # [3,5] $point.set( :x(10), :y(20) ); say $point; # [10, 20] $point.set( :x(100) ); say $point; # [100, 20]