Built-in classes in Perl 5.38

This week, the new version of Perl was announced. The new version 5.38 is extremely interesting because it introduces classes, which are built-in in the core language. The feature is currently marked as experimental, but nevertheless it was interesting for me to try it out.

Disclaimer. The war in Ukraine continues, so despite all the interesting things happening around us, my main focus now is still Ukraine. You can contribute to its success by making donations to reputable funds or by taking care of the refugees in your country.

This week, the new version of Perl was announced. The new version 5.38 is extremely interesting because it introduces classes, which are built-in in the core language. (Perl supports Object-oriented programming for long time through the use of blessed references.) The feature is currently marked as experimental, but nevertheless it was interesting for me to try it out.

For the test task I chose one of the tasks in this week’s Weekly Challenge 224. Here it is.

You are given two strings, $source and $target.

Write a script to find out if using the characters (only once) from source, a target string can be created.

Example 1

Input: $source = "abc"
       $target = "xyz"
Output: false

Example 2

Input: $source = "scriptinglanguage"
       $target = "perl"
Output: true

Example 3

Input: $source = "aabbcc"
       $target = "abc"
Output: true

This problem is good for Perl as it is about string manipulations, but to test the new classes, I had to make the solution intentionally over-engineered. So, please consider is as a program that demonstrates different features of the new Perl tool rather than the example to copy. Unlike the whole program, the algorithm itself is quite optimal. You will find it in the solve method.

Creating a class

To create a class in Perl, use the class keyword. I will write a couple of classes for the above-mentioned problem, but let’s first take a close look at one of them, a wrapper for strings (again, remember, that this is a demo program with the exaggerated structure).

class MyString {
}

Now, let us add a container to keep some data. A MyString object will contain a bare string, which can be located in a scalar variable:

class MyString {
    field $string;
}

It would be great if we could create an object and immediately set the value to its string container. One of the options is to make a field a parameter:

class MyString {
    field $string :param;
}

Now, you pass the value to the constructor as a named argument:

my $s = MyString->new(string => 'abc');

To create a method, create a method 🙂 For example, we’ll need a way to split the string into characters.

method chars {
    return split //, $string;
}

Methods are called using an arrow: $s->chars.

OK, we are ready no to complete the MyString class. For the given problem, we’ll need a few more methods.

class MyString {
    field $string :param;

    method string {
        return $string;
    }

    method chars {
        return split //, $string;
    }

    method count_chars {
        my %count;

        for my $char ($self->chars) {
            $count{$char}++;            
        }

        return %count;
    }

    method compare($other_string) {
        return $string eq $other_string->string;
    }

    method len {
        return length $string;
    }
}

Here, I created a getter called string that simply returns the original string, the len function as a synonym for the built-in Perl’s length function, the compare function to compare two strings wrapped in MyString, and finally, the function count_chars to create a hash that holds the number of occurrences of each character in the given string.

Notice how the methods use other methods and how they access the $string field. The field is accessible (on the syntax level) as a regular variable, while the method requires the $self pointer.

Hidden fields and the ADJUST method

In the second class that I created for my program in hand, I used a few other features that are available in Perl 5.38. Namely, bare fields that serve as private variables, and the ADJUST method that is called automatically after the constructor.

Here is the whole class:

class SpecialNotes {
    field $source :param;
    field $target :param;

    field $source_string;
    field $target_string;

    ADJUST {
        $source_string = MyString->new(string => $source);
        $target_string = MyString->new(string => $target);
    }

    method dump {
        return "'$source' vs. '$target'";
    }

    method solve {
        return 'true' if $source_string->compare($target_string);

        my %source_chars = $source_string->count_chars;
        my %target_chars = $target_string->count_chars;

        my $count = 0;
        for my $char (keys %target_chars) {   
            last unless exists $source_chars{$char};         
            last if $source_chars{$char} < $target_chars{$char};
            $count++;
        }

        return $count == $target_string->len ? 'true' : 'false';
    }
}

The first two fields are, actually, only used to take the strings from constructor’s parameters. For the strings you pass, the ADJUST method immediately creates two MyString objects, which are already accessible when you first call any other method of the class.

We have two methods, one for printing the debug output (dump), and another method that actually solves the problem. Let me do not explain the algorithm as I find the code quite self-explaining. When reading it, pay attention to how the class methods and fields are used.

Testing it all together

Finally, the small test code runs the solver for the input examples from the task’s description.

my @tests = (
    [abc => 'xyz'],
    [scriptinglanguage => 'perl'],
    [aabbcc => 'abc'],
);

for my $test (@tests) {
    my $note = SpecialNotes->new(
        source => $test->[0], target => $test->[1]
    );
    say $note->dump . " => " . $note->solve;
}

The program prints the following output:

'abc' vs. 'xyz' => false
'scriptinglanguage' vs. 'perl' => true
'aabbcc' vs. 'abc' => true

You can find the whole program on GitHub.

Also, see the list of solved tasked for the Weekly Challenge.

2 thoughts on “Built-in classes in Perl 5.38”

  1. Pedantic note: I keep running into this “Perl v5.38 introduces classes” soundbite. Perl has had class-based OOP since Perl 5.000 in 1994. This just introduces more familiar keywords for the normies coming from other languages. (And under the hood it doesn’t use blessed references, although ref still does the right thing.)

Leave a Reply

Your email address will not be published. Required fields are marked *

Retype the CAPTCHA code from the image
Change the CAPTCHA codeSpeak the CAPTCHA code