=head1 The Rakudo Metamodel


=head2 Warning

What follows is the current way this works in Rakudo. Parts of it may one day
become spec, other bits likely never will. All of it is liable to change as we
work out what should be spec and what shouldn't be, and also co-ordinate with
other implementations and interested parties to make sure those bits that we
determine should be specification are spec'd in a way that matches our shared
desires and needs, so far as that's possible. It goes without saying that in
doing the things described in this document, you're walking barefoot through a
construction site. For now, tread carefully, and be prepared to patch up in
response to changes.


=head2 Overview

Meta-objects are simply objects that describe parts of the object model. The
metamodel lays down the interface that these objects should provide. In Rakudo
we have several types of meta-object, all of which have an associated API
(sometimes known as a Meta-object Protocol, just to make sure the whole topic
appears to be sufficiently scary to outsiders, or something). This document
defines the API for:

=over 4

=item Packages meta-objects (representing classes, grammars, roles, etc);
ones included in Rakudo include ClassHOW, GrammarHOW and RoleHOW.

=item Attribute meta-objects (representing attributes); the default one of
these is simply called Attribute.

=item Composition meta-objects (e.g. specifying role composition algorithms)

=back

The composition model warrants a little explanation, since it is broken into
a couple of parts. We'll stick with classes and roles for now, but we define
the interface in the expectation that one day we might want to have things
that are composed into a class following some composition algorithm that may
be given a different name. Thus we talk in terms of "composables".

There are two important things. First, the actual composer - implementing the
composition algorithm - is not a part of the thing we're composing or the
thing we're composing into (that is, it is independent of the class and the
role). Second, it is up to the thing being composed (e.g. the role in the case
of composing a role into a class) to supply the composer.


=head2 Package meta-object API (aka HOW API)

This is the API for packages. When we compile something like:

    class Town is Place {
        method bar() { say "mmm beer" }
    }

Then it results in a set of calls like:

    my $temp = ClassHOW.new('Town');
    &trait_mod:<is>($temp, Place);
    $temp.^add_method('bar', anon method bar() { say "mmm beer" });
    ::Town := $temp.^compose();

Most of these are calls on the meta-class to methods that give meaning to the
keywords the user wrote in their code. The following methods are supported as
a minimum.


=over 4

=item method new($name?)

Creates something that knows how to provide its metaclass, e.g. through the
same mechanism as C<.HOW> obtains it. It need not be the final type-object that
may be installed in a namespace or lexpad - that is for compose to return. However,
there's nothing to stop it being.

Whether a new instance of the meta-class is created or not is totally up to the
implementation of the C<new> method. For the standard Raku C<class> keyword in
Rakudo, we create an instance of the meta-class and a temporary object that only
knows how to reference the meta-class instance. However, if you were doing a more
prototype-OO implementation, then you could instead have the meta-class be a
singleton and return a new object, and the object itself knows completely about
its methods, attributes and so forth, rather than this knowledge belonging to the
meta-class.

=item add_method($meta, $name, &code_ref)

Adds a method to the methods table of C<$meta> under the given C<$name> and with
the given implementation.

=item add_attribute($meta, $name)

Adds an attribute of the given C<$name> to C<$meta>.

=item add_parent($meta, $parent)

Adds the given parent to C<$meta>.

=item add_composable($meta, $composee)

Takes something that we are able to compose (for example, a role) and adds it
to the composition list. Certainly, none of the built-in implementations of
add_composable immediately perform any composition at this point. Instead,
they add the composable to a "to do" list, and at the point we call "compose"
to finish the composition of the package, and the application of all the
composables takes place. You probably want to do something similar.

=item applier_for($meta, $target)

For non-composables (that is, packages that cannot be composed into others),
this is an error. Otherwise, it returns something that we can use to apply
the current package to the target. The thing returned should implement the
composer API. It may be convenient to implement this is a set of multi subs.

=item compose($meta)

Finalizes the creation of the package. It can do any other composition-time
operations, such as role composition and calling the composition hook on all
attributes added to the package. Returns the type object that we're going to
actually install into the namespace or lexical pad, or just return if it's
an anonymous declaration.

=back

This is the declarational part of the API, however the introspection part
should also be implemented. Please see the Introspection section of S12 for
details on this.


=head2 Attribute meta-object API

This is the API that objects representing attributes should expose. The
attribute meta-object is responsible for generating any accessor and/or
delegation methods associated with the attribute.

=over 4

=item new($name, :$has-accessor, :$rw, :$handles, :$build, :$type)

Creates a new attribute meta-object, initialized with the name, whether or
not the attribute has an accessor, whether or not that accessor 'is rw' and -
if there was a handles trait modifier on the attribute - the handles trait
modifier.

=item compose($meta-package)

Takes the attribute and does any final composition tasks (such as installing
any accessor methods and/or delegators). The parameter is the meta-object of
the package that the attribute belongs to; you can call .add_method on it to
add methods, for example.

=back


=head2 Composer meta-object API

The composer is responsible for composing something composable (in standard
Raku, that's a role) into some other object (perhaps a class or another 
role or an instance). The minimal interface need only support one method,
but it may well be that a composee and a composer choose to share knowledge
of more than this (for example, a "requires" or "conflicts" list).

=over 4

=item apply($target, @composees)

Applies all of the composees to the target, or throws an exception if there
is a problem with doing so. It's totally up to the composer exactly what it
does; the default composer for Raku roles will construct a single
intermediate role and then compose that into the target, for example. Since
the model is intended for more general composition-y things rather than just
roles as are commonly defined today, we choose to give the composer a view of
all of the composees.

=back


=head2 Metaclass Compatibility

Warning: Conjectural.

One rather complex issue we run into is what happens if you want to inherit
from something that has a different metaclass. For now, we require that if
some class S isa T, then also S.HOW isa T.HOW. This means that all other types
of class-ish things that want to have a custom metaclass should subclass
ClassHOW (either directly or transitively). Thus, this is fine:

    class A { }
    thingy B is A { }
    otherthingy C is B { }

If the following is true:

    OtherThingyHOW.isa(ThingyHOW) and ThingyHOW.isa(ClassHOW)


=head2 Composer Compatibility

Warning: Conjectural.

TODO: Provide a solution to the problems described here.

Given it's the things we compose that determine what composer to use, we may
easily run into a situation where different things want a different composer.
At some level that's OK - if we want to support a more general notion of
"things that do something composition-ish" then it is probably too restrictive
to just always make this an error in the long run. For now, however, we do
just that; when we have a good solution, we can relax the restriction.

We do have the nicety that once we hit runtime, since composition is flattening
by nature, we don't have any relationship at runtime with something that was
composed in (besides keeping it in our list of "things that we composed"). Thus
the problem of the behaviors of two different appliers is only
a composition-time issue and not a runtime one.


=head2 Associating a package declarator with a metaclass

Rakudo provides two levels of hookage for creating new types of package
declarator. You will very likely only need this one, which is the HOW map,
%*HOW. This is simply a hash that maps the name of a scope declarator to the
name of the HOW to create. At the entry point to your derived grammar, you
should temporize the current HOW hash from the calling language, and add
mappings from names of package declarators that you will introduce to the HOW
to use. By default, this hash contains things like:

    { class => 'ClassHOW', role => 'RoleHOW' }
    
It's completely fine for multiple package declarators to map to the same HOW
- you may just wish to introduce a new one as better documentation but not
need to do anything more special in terms of the meta-model. Note that your
rule for parsing the scope declarator sets the name of the thing in this map
in the $*PKGDECL global. For example, here is one from STD.

  token package_declarator:role {
      :my $*PKGDECL ::= 'role';
      <sym> <package_def>
  }

You should do the same (and it's probably nice if what you set matches the
name of the symbol you parse).


=head2 Meta-programming Example: Adding AOP Support

Note that this currently does not work in Rakudo, and will probably change a
bit. It's purpose is mostly a thought experiment to try and help sanify the
design of the metamodel.

    slang AOP {
        method TOP {
            temp %*HOW;
            %*HOW<aspect> := AspectHOW;
            my $lang = self.cursor_fresh( AOP );
            $lang.comp_unit;
        }
        token package_declarator:sym<aspect> {
            :my $*PKGDECL := 'aspect';
            <sym> <package_def>
        }
    }

    class AspectComposer {
        method apply($target, @aspects) {
            my @wrappables = $target.methods(:local);
            for @aspects -> $aspect {
                for $aspect.method_wrappers.kv -> $name, $wrapper {
                    my ($wrappee) = @wrappables.grep({ .name eq $name });
                    if $wrappee {
                        $wrappee.wrap($wrapper);
                    }
                    else {
                        die "No sub found to wrap named '$name'";
                    }
                }
            }
        }
    }

    class Aspect {
        has $.HOW;
        has $.Str;
        method WHAT() { self }
        method defined() { False }
    }

    class AspectHOW {
        has %.method_wrappers;

        method new($name) {
            return Aspect.new(
                HOW => self.bless(*),
                Str => $name ~ "()"
            );
        }
        
        method add_method($obj, $name, $method) {
            $.method_wrappers{$name} = $method;
        }
        
        multi method composer_for($obj, $ where { .can('methods') }) {
            return AspectComposer;
        }
        
        multi method composer_for($obj, Object) {
            die "Can only apply aspects to things that expose methods";
        }

        method compose($obj) {
            return $obj;
        }

        method add_attribute($meta, $attr) {
            die "Aspects do not support attributes";
        }

        method add_parent($meta, $parent) {
            die "Aspects do not support inheritance";
        }

        method add_composable($meta, $composable) {
            die "Aspects do not support being composed into";
        }
    }

This could then we used as something like:

    use AOP;

    aspect LogToStderrToo {
        method log($message) {
            $*ERR.say($message);
            nextsame;
        }
    }

    class ErrorLog does LogToStderrToo {
        method log($message) {
            my $fh = open("log", :a);
            $fh.say($message);
            $fh.close;
        }
    }

Note that a usable implementation would want a bit more than this, and have
many other design considerations.


=head2 Influencing package code generation

Note: This is highly Rakudo-specific and very likely to remain that way.
The good news is that you won't need to do it often.

Rakudo has a compile-time representation of the package currently being
compiled. This is the thing that ends up actually generating
the code - PAST nodes - that make the calls on the metaclass. By default, we
always create an instance of Perl6::Compiler::Package, apart from for roles
and modules, for which we need to do some slightly different code generation
- those use a subclass of it, such as Perl6::Compiler::Role. You may modify
%*PKGCOMPILER, again keying on $*PKGDECL, to specify something other than the
default. You should then write a subclass of an existing handler for this and
implement the same interface (plus any other bits you'll need - it's just a
class).
