DEV Community

Cover image for Definitely How What Where, Who?
Elizabeth Mattijsen
Elizabeth Mattijsen

Posted on

Definitely How What Where, Who?

This is part thirteen in the "Cases of UPPER" series of blog posts, describing the Raku syntax elements that are completely in UPPERCASE.

This part will discuss the various introspection methods that you can use on objects in the Raku Programming Language. Note that in some documentation these methods are also referred to as Metamethods.

Turtles All The Way Down

In Raku everything is an object, or can be thought of as an object. An object is an instantiation of a class (usually made by calling the new method on it). A class is represented by a so-called "type object". Such a type object in turn is an instantation of a so-called meta class. And these meta classes are themselves built out of more primitive representations.

Going this deep would most definitely be out of scope for these blog posts. But yours truly does intend to go there at some point in the future.

WHAT

The WHAT method returns the type object of the given invocant. Not much else to tell about it really.

say 42.WHAT;     # (Int)
say "foo".WHAT;  # (Str)
say now.WHAT;    # (Instant)
Enter fullscreen mode Exit fullscreen mode

HOW

The HOW method returns the meta-object of the class of the given invocant. The HOW (b)acronym stands for "Higher Order Workings". It allows one to introspect the class of the invocant.

say 42.HOW.name(42);        # Int
say "foo".HOW.name("foo");  # Str
say now.HOW.name(now);      # Instant
Enter fullscreen mode Exit fullscreen mode

Note that the invocant of the HOW method needs to be repeated in the introspection method's call as the first argument. Why? Well, this is really to be possibly compatible with future versions of Raku.

Since one is usually only interested in the introspection aspect of HOW, a shortcut method invocation was created that allows one to directly call the introspection method without needing to repeat oneself: .^:

say 42.^name;     # Int
say "foo".^name;  # Str
say now.^name;    # Instant
Enter fullscreen mode Exit fullscreen mode

Some other common introspection methods are mro (showing the base classes of the class of the value) and methods (which returns the method objects of the methods that can be called on the value):

say 42.^mro;           # ((Int) (Cool) (Any) (Mu))
say 42.^methods.sort;  # (ACCEPTS Bool Bridge Capture Complex...
Enter fullscreen mode Exit fullscreen mode

Note that these meta-classes are classes themselves, so can have meta-methods on them called as well:

say 42.HOW.^name;  # Perl6::Metamodel::ClassHOW
Enter fullscreen mode Exit fullscreen mode

Yeah, there's still some legacy code that will need renaming under the hood!

WHERE

The WHERE method returns the memory address of the invocant. It is of limited use in the Rakudo implementation as the memory location of an object is not guaranteed to be constant. As such, it is intended for (core) debugging only.

say 42.WHERE;  # 2912024602280 (or some other number)
Enter fullscreen mode Exit fullscreen mode

VAR

In the previous blog post the Scalar object was described. But the Scalar objects are nearly invisible. How can one obtain a Scalar object from a given variable? And find out its name from that?

The "secret" to that is the VAR method.

my $a;
say $a.VAR.^name;  # Scalar
Enter fullscreen mode Exit fullscreen mode

Each Scalar object provides at least these introspection methods: of, name, default and dynamic.

my Int $a is default(42) = 666;
say $a;              # 666
say $a.VAR.of;       # (Int)
say $a.VAR.name;     # $a
say $a.VAR.default;  # 42
say $a.VAR.dynamic;  # False
Enter fullscreen mode Exit fullscreen mode

The of method returns the constraint that needs to be fulfilled in order to be able to assign to the variable. The name method returns the name of the variable. The default method returns the default value (Any if none is specified).

The dynamic method returns True or False whether the variable is visible for dynamic variable lookups. This usually only returns True for variables with the * twigil.

WHO

The WHO method (for "who lives here?) is actually a bit of a misnomer. It should probably have been called OUR because it returns the Stash of the type object of the invocant. And a stash is an object that does the Associative role, and as such can be accessed as if it were a Hash. And the stash of a type object is the same namespace as our inside that package.

So for instance if you would like to know all classes that live in the IO package:

say IO.WHO.keys.sort;  # (ArgFiles CatHandle Handle Notification Path Pipe Socket Spec Special)
Enter fullscreen mode Exit fullscreen mode

Of course, you would know them more by their complete names such as IO::ArgFiles, IO::CatHandle, IO::Handle, etc. In fact the :: delimiter is shortcut for using WHO:

say IO.WHO<Handle>;  # (Handle)
say IO::Handle;      # (Handle)
Enter fullscreen mode Exit fullscreen mode

And that goes even further: foo:: is just short for foo.WHO:

say IO::.keys.sort;  # (ArgFiles CatHandle Handle Notification Path Pipe Socket Spec Special)
Enter fullscreen mode Exit fullscreen mode

A little closer to home: how would that look in a package that you define yourself and have an our scoped variable in there:

package A {
    our $foo = 42;
}
say A.WHO<$foo>;  # 42
say A::<$foo>;    # 42
say $A::foo;      # 42
Enter fullscreen mode Exit fullscreen mode

The careful reader will have noticed that package was used in the example. A very simple reason: class, role, grammar are all just packages with different HOWs. And WHO doesn't care why kind of package it is.

REPR

The REPR method returns the name of the memory representation of the class of the invocant. For most of the objects this is "P6opaque".

This is basically the representation used by class and its attribute specifications.

The NativeCall module provides a number or alternate memory representations, such as CStruct, CPointer and Cunion. Native arrays also have a different representation (VMArray).

say 42.REPR;  # P6opaque
my int @a;
say @a.REPR;  # VMArray
Enter fullscreen mode Exit fullscreen mode

When a class is defined, it gets the P6opaque representation by default.

class Foo { }
say Foo.REPR;  # P6opaque
Enter fullscreen mode Exit fullscreen mode

Unless one is doing very deep core-ish work, how an object is represented in memory should not be of concern to you.

DEFINITE

The DEFINITE method returns either True or False depending on whether the invocant has a concrete representation. This is almost always the same as calling the defined method. But in some cases it makes more sense in Raku to return the opposite with the defined method.

An example of this is the Failure class:

say Failure.new.DEFINITE;  # True
say Failure.new.defined;   # False
Enter fullscreen mode Exit fullscreen mode

In general the defined method should be used. The DEFINITE method is intended to be used in very low-level (core) code. It's not all uppercase for nothing!

The reason Failure.new.defined always returns False is to make it compatible with with.

Macroish

All of the introspection "metamethods" described in this blog post are actually parsed as macros directly generating low-level execution opcodes. This is really necessary in some cases (as otherwise information can be lost), and in other cases it's just for performance.

This doesn't mean that it's not possible to call this introspection functionality as a method: you can. For example:

my $a;
say "macro: "  ~ $a.VAR.name;      # macro: $a
say "method: " ~ $a."VAR"().name;  # method: $a
Enter fullscreen mode Exit fullscreen mode

Furthermore for consistency, the same functionality of these methods is also available as subroutines.

my $b;
say "sub: " ~ VAR($b).name;  # sub: $b
Enter fullscreen mode Exit fullscreen mode

So if you're more at home in imperative programming, you can do that as well!

Conclusion

This concludes the thirteenth episode of cases of UPPER language elements in the Raku Programming Language, the sixth episode discussing interface methods.

In this episode the following macro-like introspection methods were discussed (in alphabetical order): DEFINITE, HOW, REPR, VAR, WHAT, WHERE, WHO.

Stay tuned for the next episode!

Top comments (0)