Experiment in the Perl 6 REPL

Perl 6 has a REPL. That’s a tiny, language-specific shell that Reads a line, Evaluates it, Prints the result, and Loops to do it again. It can remember the previous results in the same session.

Let’s start with something simple. Run the perl6 command with no arguments and wait for the prompt:

$ perl6
To exit type 'exit' or '^D'
>

I can try the usual “Hello World” program:

> 'Hello World!'
Hello World!

I can try it with say, which outputs the string with a newline on the end. That’s how I would need to output it inside a program; inside the REPL, that doesn’t look different than
what I already did:

> say 'Hello World!'
Hello World!

In Perl 6 land, a string is an object, so I can call methods on the string. Perl 6 uses the dot as the method call operator:

> 'Hello World!'.say
Hello World!

What else can I do with ? Perl 6 values are themselves objects. The WHAT “meta” method knows what thingys are:

> 'Hello World!'.WHAT
(Str)

The (Str) is the name of the class in parentheses. I don’t need to know the type of thing it is to figure out what I can do. Or, read another way, I don’t need to look at the docs for Str to see what methods I can call. The .^methods method (ahem) tells me what the object can respond to:

> 'Hello World!'.^methods
(BUILD Int Num chomp pred succ simplematch match ords samecase
samemark samespace word-by-word trim-leading trim-trailing trim
encode NFC NFD NFKC NFKD wordcase trans indent codes chars uc lc tc
fc tclc flip ord WHY WHICH Bool Str Stringy DUMP ACCEPTS chop
starts-with ends-with substr-eq contains indices index rindex Numeric
gist perl comb subst-mutate subst lines split words)

That .^methods looks weird, but it’s really just a shortcut for .HOW.methods. The Higher Order Workings class has many ways to introspect a thingy. This is an amazing boon to learning the language since you can, at least partly, discover things about the objects from inside the language. All your Ruby friends will say “Welcome to 1995!”.

Notice that say is not in the list. By default, .^methods doesn’t show methods from certain base classes. I can ask for different slices of that list by adding the :all adverb:

> 'Hello World!'.^methods(:all)
(BUILD Int Num chomp pred succ simplematch match ords samecase
samemark samespace word-by-word trim-leading trim-trailing trim encode
NFC NFD NFKC NFKD wordcase trans indent codes chars uc lc tc fc tclc
flip ord WHY WHICH Bool Str Stringy DUMP ACCEPTS chop starts-with
ends-with substr-eq contains indices index rindex Numeric gist perl
comb subst-mutate subst lines split words trans fmt comb acosech cotan
sin fc contains EVAL cosec atan2 abs ord trim-trailing ords acotan
asec ceiling unpolar acosec acos indices log10 exp match subst chrs
acosh truncate acotanh Num path floor UInt words flip asin codes
univals asech chomp split cotanh ...)

That’s right, I said adverb. Things that start with a colon are modifiers for an action. You can worry about those later. In this case, :all modifies how .^methods responds.

Still, I don’t see say in that list, which is truncated with .... Perl 6 displays the result as a “gist”, or, the general idea of the result in a human-readable form. It’s the same thing as this:

> 'Hello World!'.^methods(:all).gist

This gist an amazing feature. I’ve wished that Data::Dumper was built into Perl 5 so many times. Now it’s available to most objects automatically, but it is not going to overload you with output. I’ll have to do a bit of work on my own for that.

If that’s too high-level for you, the perl method is closer to the internal representation. You can run this yourself if you’d like to see the very long and complicated output that’s pretty useless to all but 11 people in the world:

> 'Hello World!'.^methods(:all).perl

But, I want to find that say method. What do I get back from ^methods()? I call WHAT again and see that I get a List object:

> 'Hello World!'.^methods(:all).WHAT
(List)

That’s a list, but what’s in the list? I can call map on the List. I cheat a bit here because I know that there should be something like map and I looked at the documentation. I’d much rather discover that through exploration, but that’s how it is at the moment:

> 'Hello World!'.^methods(:all).map( {.WHAT.gist} ).unique
((Submethod) (Method) (Method+{Callable[Int:D]}) (Method+{<anon|140546676469920>}))

The map gets each item in the list and passes it to the block argument. Perl 6 does some impressive stuff with blocks, but for now think of it as an inline subroutine. Inside this map, I call .WHAT.gist. What’s up with the leading dot?

Perl 6 has a default variable, which it calls the topic. If there’s no object in front of the method-calling dot, it uses that topic.

Once I’ve constructed that list I call unique to make a list of just the types it saw. Each thing is a some sort of Method.

A Method knows its name (if it has one). This in itself is quite interesting. If I call can on an object and that object can respond to a method of that name, it gives me back a list of methods? (A list? Yep! Because Perl 6 has multi dispatch based on the arguments I pass!). Even though I have a list of one item, I only want to first item. I store that in $method (which I must declare before use):

> my $method = 'Hello World!'.can( 'say' ).first
say

The REPL remembers what I stored in that variable. It’s a Method and I can ask it its name. I can also use the variable in place of the literal method name to operate on my string:

> $method.name
say
> 'Hello World!'.$method
Hello World!

It’s a bit easier to see if I use a different method, such as uc to uppercase

> my $method = 'Hello World!'.can( 'uc' ).first
uc
> 'Hello World!'.$method
HELLO WORLD!

This is very exciting! It might not seem like much, but when these things are objects that know things about themselves, a big world of programming opens up.

But, I started off looking for say but I haven’t found it yet.

I can make a new list of just the names by calling map and sort that. When I give only one argument to the block in sort, it uses that result to compare elements. It also remembers that value so it does not calculate it again (so, no Schwartzian Transform needed!):

> 'Hello World!'.^methods(:all).map( { .name } ).sort( { .lc } ).unique.join( "\n" )

This creates a really long string, but it’s all the methods available on "Hello World". I can make a shorter list with grep by looking for things that have sa at the start of the string:

> 'Hello World!'.^methods(:all).map( {.name} ).grep( { /^^ sa/ } ).join( "\n" )
samecase
samemark
samespace
samecase
say

And, I’ve found say! It took me a bit, but I was able to find out quite a bit starting with just the object.

I can define my own class with some attributes. Here’s a Butterfly class that has a name and a type attribute. I
can create an object in that class, then look at the

> class Butterfly { has Str $.name; has Str $.type; }
(Butterfly)
> my $camelia = Butterfly.new( name => 'Camelia', type => 'Monarch' );
Butterfly.new(name => "Camelia", type => "Monarch")
> $camelia.WHAT
(Butterfly)
> $camelia.^attributes
(Str $!name Str $!type)

I could keep going like this to try bits of Perl 6. I don’t have to write programs for the very short experiements. Personally, I like to keep the experiments in files because I keep changing things. It’s better than retyping.

2 comments

  1. Hi Brian, trying the examples in P6 (-v reports, ‘This is perl6 version 2014.07 built on parrot 6.6.0 revision 0’) on RaspberryPi… (This is a really old build.) The below code returns nothing…

    > ‘Hello World!’.^methods(:all).WHAT
    >

    returns nothing. Trying [0] …

    > ‘Hello World!’.^methods(:all).WHAT.gist
    > (Parcel)

    I think the version of rakudo/parrot I’ve installed, stock on latest P6 pre-build on RPi Raspian is borked. I think I’ll build from the source to see what happens in a later build. Thanks for adding these posts, great way to go through Perl6.

    [0] SO

Leave a Reply

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