Author: barney@cpan.org
Title: Assign meta operator
Provide dedicated syntax for declaring meta properties of misc entities.
Note: This is part of larger set of RFCs (Context Oriented Programming) which can be reused by other (unrelated) RFCs.
Note 2: Few times I'll mention Ovid's Cor proposal where this RFC may overlap (conflict) with it using different expressivity (showing support to his effort).
Current perl ecosystem has many approaches how to specify meta properties in different entities
-
package
- perl core - set package variables (
@ISA
,@EXPORT
, ...) - Moo/Moose - functions like
extends
,with
- Ovid's Cor proposal - keywords
isa
,does
- perl core - set package variables (
-
function / method
- perl core - prototypes
- perl core - signatures
- perl core - attributes
- perl core -
return bless
(that function is in fact constructor) - Export libraries - reference function in
@EXPORT
, ... - Moo/Moose - functions like
around
,override
, ... - Ovid's Cor - method modifier like
abstract
,private
, ...
-
variable
- perl core - attributes
- perl core - magic (eg
Readonly
) - perl core -
bless
,tie
- perl core - intention often implemented by using
//
,//=
, ... - Moo/Moose - function
has
Moose::Util::apply_all_roles
- Ovid's Cor - keyword
has
Definition of binary define-meta operator will provide unified way to express intention (which leads to non negligible improvement to code readability)
New chainable, left associative binary operator LHS := meta property expression
:meta-property-name => meta-property-value
- basic variant, set value to property
:meta-property-name
- set value of boolean property to true
not :meta-property-name
- set value of boolean property to false
meta-property-value
- when property name is omitted,
:default
is used
- when property name is omitted,
when (condition) => value
expression can be used for conditional assignment.
Condition is stored and evaluated when given meta property is evaluated.
basic variant can contain any number of when expressions
LHS := :property
when (cond-1) => value
when (cond-2) => value
=> otherwise
short default version
LHS :=
when (cond-1) => value
when (cond-2) => value
=> otherwise
Short boolean variants can contain short when conditions. Those conditions are evaluated as OR
LHS := :required
when (cond-1)
when (cond-2)
LHS := not :required
when (cond-1)
when (cond-2)
Meta property name is an identifier.
When used in meta property expression meta property name should be prefixed with colon (behaves like sigil - whitespaces allowed).
C-API v0 should provide:
is_meta_property_set (object, meta_property)
list_meta_properties (object
meta_property_value (object, meta_property, context)
For purpose if this API meta property is represented by package literal (package name).
Meta properties available in current perl code are available via %{^Meta}
- key is a meta property name used in code
- value is package literal
It should be possible to populate %{^Meta}
from sub import
Not every meta property used in examples is also specified in this RFC
Compile time
package Foo v1.0.0
:= : extends => Parent1
:= : extends => Parent2
:= : does => Role1
:= : does => Role2
:= : rest_archetype => Rest::Archetype::Collection::
:= : rest_path => '/internal/foo/'
{
}
Runtime
package Foo v1.0.0 {
__PACKAGE__ := :does => Role3 (with => parameter)
if $ENV{USE_ROLE3};
}
Foo:: := :does => Role4;
Compile time
sub foo
:= :lvalue
:= :is => CORE::Array::
:= :public # (Cor attribute)
:= :shared # (Cor attribute)
Runtime
sub foo {
__SUB__ := :is => CORE::Hash::;
}
&foo := not :available;
# Mimic Sub::Override
local &foo := :bind => sub { logger->log (@_); goto &foo };
my $bar := :is => CORE::Number:: = 10;
$bar := :readonly;
has $var
:= :is => CORE::Number::
:= :is => CORE::Defined::
:= :default => -1
:= :readonly
:= :private
;
Package literal CORE::Meta::Extends::
When applied on package
package Foo v1.0.0 := :extends => Bar {
}
package Foo v1.0.0 {
__PACKAGE__ := :extends => Bar;
__PACKAGE__ := :extends => Baz
if $ENV{USE_BAZ};
}
Currently: simply push its argument to @ISA
When applied on sub
sub foo := :extends => &bar { }
Declares function foo
with same prototype/signature as bar
.
Can be used to implement callbacks (eg: Getopt::Long)
Although :extends
looks similar to :is
, purpose of :extends
is
to reuse and extend parent context whereas purpose of:is
is to
restrict context by imposing constraints.
Package literal CORE::Meta::Does::
Similar to :extends
but applies role on class or instance.
package Foo
:= :does => Role1
:= :does => Parameterized::Role (...)
{ }
package Foo {
__PACKAGE
:= :does => Role1
:= :does => Parameterized::Role (...)
}
$object := :does => Dynamic::Role;
Package literal CORE::Meta::Is::
Assign constraint to LHS.
Validation throws an exception (dies) when validation fails.
When applied on variable
$var := :is => Constraint;
Appends constraint to list of variable constraint and validates it.
When applied on variable declaration
my $var := :is => Constraint;
has var := :is => Constraint;
Appends constraint to list of variable constraint. Constraints are evaluated when new value is assigned to variable.
When applied on sub
sub foo := :is => Constraint {}
sub foo {
__SUB__ := :is => Constraint;
}
appends constraint to function. Constraints are evaluated when function returns.
Package literal CORE::Meta::Required::
Boolean property, by default false.
Applicable to variables or class properties.
package Foo {
has foo := :required;
has bar := not :required when ($foo % 2);
}
sub authenticate {
has login := :required;
has password := :required;
}
When set on variable it throws an exception unless there already was value assigned to it.
GetOptions (...);
$option_password := :required when (exists $option_login);
# See extended behaviour of keyword exists
Package literal CORE::Meta::Available::
Boolean property, by default true.
When set to false, assignment to variable will cause runtime warning.
sub authenticate {
has login := not :available;
has password := not :available;
has oauth := :required;
}
Package literal CORE::Meta::Default::
Specify default value of variable / property. Value expression is treated as an block and is evaluated every time when needed.
has protocol := :default => 'https';
has protocol := 'https';
$foo->protocol := 'https';
$foo := 'https';
:default
can be specified multiple time, last assignment will be used.
If last assignment cannot be used (eg it contains only when
clauses),
its previous assignment is used.
Specifying :default
on assigned value has no effect, it should produce warning.
Unlike defined-or operator default value accepts undef
as valid value.
Package literal CORE::Meta::Bind::
Binds object with another object (effectively creating alias)
When used on package literal it creates an alias for another package. Alias can be used to call methods as well as class instance operator
Local::Alias:: := :bind => Long::Package::Name::As::Usual::In::World::Like::XXX;
# calls Long::Package::Name::As::Usual::In::World::Like::XXX->new
my $foo = Local::Alias::->new ();
# following is evaluated as true
$foo isa Local::Alias::
$foo isa Long::Package::Name::As::Usual::In::World::Like::XXX::
Long::Package::Name::As::Usual::In::World::Like::XXX:: isa Local::Alias::
When used on subs, variables, data path it creates an variable alias
my %foo = (baz => 1);
my %bar := :bind => %foo;
my $baz := :bind => $bar{baz};
sub foo := :bind => &CORE::say;
foo $baz;
# acts like say, prints 1
$baz++;
foo $foo{baz};
# acts like say, prints 2
When used on sub it also shares prototype
sub foo (&) { ... }
sub bar := :bind => &foo;
foo { ... };
bar { ... };
When specified on package, acts like Moose __PACKAGE__->meta->make_immutable
When specified on variable, makes variable readonly (or write once if has no value yet)
When specified on sub, signals that sub only reads arguments
in package context when :=
is detected as unary operator use __PACKAGE__
as an implicit LHS. Similarly when in sub block, use __SUB__
package Foo {
:= : extends => Bar::
:= : does => Role
;
}
sub foo {
:= :is Baz::
;
}
- java annotations
- this RFC provides most of expressivity provided by java annotation
- in java one annotates data-type
- annotations cannot by specify dynamically
- this RFC provides most of expressivity provided by java annotation
Specify callback what to do when property is set
usage is dedicated operator is easier to read (and parse as well)
foo $bar := 1, @baz := 2 .. 3;
and more derived from https://github.com/happy-barney/perl-poc/tree/perl-features/COP