develooper Front page | perl.perl5.porters | Postings from June 2021

Re: RFC: define meta operator

Thread Previous | Thread Next
June 14, 2021 13:42
Re: RFC: define meta operator
Message ID:
Hi Nicholas, thanks for comment

On Mon, 14 Jun 2021 at 12:38, Nicholas Clark <> wrote:

> On Sat, Jun 12, 2021 at 10:12:39PM +0200, Branislav ZahradnĂ­k wrote:
> > Note:
> > This is part of larger set of RFCs (Context Oriented Programming)
> > which can be reused by other (unrelated) RFCs.

True. This one is very low level vehicle.

> Parts of this are difficult to make sense of without those other RFCs.
> What is extracted here feels more like the Meta-Object-Protocol syntax,
> rather than the interface average users would use most of the time.

Define average user :-)
Depends on property, some should not be used at all some are designed to be
used daily.

There are corner cases for each usage. From my experience allowing corner
like rebless (in controlled scope, eg framework guts) saves lot of time
when solving
problems current code was not designed for. Also there is no need for
special syntax
for each (new) corner case - therefore generic operator.

On other hand expected usage in named argument syntax will be on daily base.

Added value of this operator is that it identifies its lhs.

> > 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).
> This RFC is part of something much larger in scope than COR. Also, COR is
> further along - there are working prototypes to explore the design. Are
> there prototypes for any parts of this proposal? It's hard to think about
> it
> from just design documents.

> Also, this proposal is (re)using core attributes such as :public and
> :shared.

Usage of :public and :shared is to show alternative syntax, treating them
as just another meta properties

> COR isn't firm yet. PSC are confident that (something like) COR will land
> in
> the core at some point, and hence *its* *eventual* choice of terms, and
> definitions for them, will have to be the core's canonical definitions of
> these, to avoid inconsistency and ambiguity. So to me it seems maybe
> premature to build massively on a foundation that is might move.

Hm, yes, this proposal will affect currently available COR (examples of
modified syntax)

class Foo
  := :extends Bar
  := :does Role

has $property
  := :is => Constraint
  := :private

Speaking about core canonical definition:
I noticed that I accidentally removed section about (custom) meta property
Generally I favour "name => package literal handler" approach, where
default COR mapping may look like

  private => CORE::COR::private::

with capability to specify own properties

   use pragma property => package literal, ...;

Allowing to specify (even override) behaviour of meta property.

> > 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`
> >
> > - function / method
> >   - perl core - prototypes
> >   - perl core - signatures
> >   - perl core - attributes
> >   - 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)
> You're proposing a syntax to unify
> * isa
> * does
> * function signatures
> * function attributes
> * bless
> * tie
> * exports
> and others. These are many disparate things.

> You're proposing a single syntax to make different things look similar.
> This removes clues about what is going on. This doesn't make code easier
> to read.

On C API (or better - on perly.y API) yes.

For others, usage of := operator provides more options. At least one -
runtime modification.
Speaking of loosing intention, there always will be identification of meta

Examples how it should look like

package Foo := :extends Bar;
class Foo := :extends Bar;
role Foo := :extends Bar;
Foo:: := :extends Bar;
sub foo := :extends &Foo::callback;

package Foo := :does Role;
class Foo := :does Role;
role Foo := :does Role;
Foo:: := :does Role;
$obj := :does Role;

function signatures:
following RFC to specify block public API will obsolete them and allows
real named arguments, eg
List::Util::mesh @keys := 1 .. 3, @values := 'a' .. 'c';

function attributes:
end game: meta define operator should replace them for fully covering their
functionality plus adding

bless, tie:
No plan to replace them, only to extend them.
from COP-proposal point of view both connects multiple contexts into one
access point.

export part can be treated as attributes
import part, similar to bless/tie, connects multiple contexts

> I can't agree with your key motivation of "non negligible improvement to
> code readability".

I agree that readability is subjective.
Some people even think that code is readable if your IDE allows you to
click on symbol ...

Premise of improvement of readability in this proposal comes from facts:
- most important part of statement is always first
- optional parts never precedes mandatory part
- meta property definition starts with ':=' (as counterpart to '->' method

So unlike for example C or Java, when specifying meta properties, modified
object will always
be first or second symbol (first in runtime, second in compile time)

For example:

    := :public
    := :static
    := :readonly
    := :default => new KafkaYammerMetrics()

> I feel strongly that this plan will make everything harder to read. It's as
> if you're suggesting that everyone should program in Moose's Meta Object
> Protocol *directly*, instead of using all the syntax it adds to make
> easy things easy.

I'm not suggesting that, I would like to "be able" to do that - when
(as you mentioned already, some parts of this RFC may look like overkill
without subsequent COP usage)

> > `when (condition) => value` expression can be used for conditional
> assignment
> PSC are confident that given/when will be reworked, possibly substantively.
> We can't be sure of the exact semantics of future behaviour, of even if we
> will stick to the current names. Building a proposal that (implicitly)
> relies on the status quo is not a good idea yet.

This proposal doesn't rely on behaviour or existence of when in a rest of
It treats and evaluates it on its own.

I can use word `if` if you like but that will make inconsistencies unless
`unless`, `elsif` and `else`
will be used as well.

> > C-API v0 should provide:
> > - `is_meta_property_set (object, meta_property)`
> > - `list_meta_properties (object`
> > - `meta_property_value (object, meta_property, context)`
> Fundamental to this proposal seems to be that this C API exists.

Yes, C API and ':=' operator.

> Is there any plan for *how* these properties should be stored internally?
> Having that is the foundation for the implementation, but there doesn't
> seem
> to be any idea for this other than these C prototype declarations.

Properties for one object will be stored in simple key value storage, where
key will be
C representation of package literal (quality of glib's quark) and value
will be OP *.

Expected cardinality is that only very small portion of values will have
meta properties
specified so storing them in key => value store (where key is a C pointer
of object and
value is earlier mentioned property storage) will suffice.

> Related to this - not everything in Perl is "first class". You can take
> references to functions and closures. You can take references to regexs.
> You can't take references to blocks, or to stack frames, but in some of the
> other messages you've sent, you are using syntax that wants to set
> properties on things like these, which can't (currently) be done in any
> way unified with CODE, ARRAY, HASH, SCALAR or Regexp.

I use two types of contexts: named and unnamed.
Named are functions, packages, variables. You can use them as LHS
of this operator. You can also address them in runtime.

Unnamed is everything else. Properties of such objects can be specified
only in compile time via proxy (eg: via keywords my/local/has)

Stack frames mentioned in COP are data stack frames.
Those differs from execution stack frames - they behave like symbol tables
and they have child -> parent relations.

Now as you mentioned that, I realized that I may need package literal as
first class.

If still unclear, please ask about exact line or ask for exact example.

> >       sub foo
> >               := :lvalue
> >               := :is => CORE::Array::
> >               := :public # (Cor attribute)
> >               := :shared # (Cor attribute)
> > ```
> >
> > Runtime
> > ```
> >       sub foo {
> >               __SUB__ := :is => CORE::Hash::;
> >       }
> These aren't defined by the Perl core. It's not meaningful to specify meta
> operators that act on something that isn't yet itself defined.

> >       has $var
> >               := :is => CORE::Number::
> >               := :is => CORE::Defined::
> This is implying a type system exists in Perl 5, and that "Number" makes
> sense in it. Again, that's not a given - Perl 5 simultaneously has
> storage representation (eg IV, PVIV, PVNV, PVMG, PVLV) and flags that are
> set to indicate which type (or types, *plural*) that value is.

I do not operate with term `type` but with term `Constraint`.
I did not specify it now, I only defined evaluation order (ie behaviour of

Definition of constraints should be stand-alone RFC and I consider this one
its prerequisite.

> There's not one obvious way to map those two internal hierarchies into a
> sane Perl-space type system.
> > ### :required
> >
> > ```
> >       package Foo {
> >               has foo := :required;
> >               has bar := not :required when ($foo % 2);
> I know from chatting with Jonathan that efficiently implementing any sort
> of
> complex constraint like this is a real stinker, and something you have to
> design in from the start (as part of your multi-dispatch). We don't have
> multi-dispatch - anything like this is likely to be extremely inefficient.

I agree, multidispatch in dynamic dispatch languages is anything by simple.
Therefore later in COP I operate with assign only values and I'm solving
via (lazy) dependencies.

For a sake of efficiency I operate with data stack in COP - result of every
constraint is stored
in stack frame where its data were modified, so for every value every
constraint is not evaluated
more then once. Details and related optimizations are imho out of scope of
this RFC.

Anyway, language should not prevent users to write inefficient code.
On other hand, language should allow users to deliver their intention as
soon as possible.

> >       sub authenticate {
> >               has login := :required;
> >               has password := :required;
> >       }
> > ```
> >
> > When set on variable it throws an exception unless there already was
> > value assigned to it.
> meaning
>     die "Constraint failure"
>        if !defined $option_password && unexpressable_thing $option_login;

Let look forward into new grammar proposal (COP related): ${: . / $login }

die '$login required in authenticate' unless exists ${: . / $login };
die '$password required in authenticate' unless exists ${: . / $password };

(typo detected: both has should by followed by $)

> This line alone would warrant an RFC - this is proposing a whole new syntax
> paradigm for thinking about optional values and the constraints between
> them.

> To me, this feels about as Perl-ish as Raku. As in, clearly Perl-ish.
> But not Perl.

I agree, this leads to new Perl.
Whole COP idea leads to new programming paradigm.

> > ### :readonly
> > When specified on variable, makes variable readonly (or write once if
> > has no value yet)
> Write once - that's not the same defintion of readonly that any other
> language I know uses. "Must have an initialiser" is the common way.

For now let's stick with "Must have an initialiser".
May be harder to explain/implement behaviour with class properties
in constructor, "Must be initialized before first read" is more precise.

> > When specified on sub, signals that sub only reads arguments
> I feel that this is conflating different concepts into one word.
> "Only reads arguments" means what - that it's not allowed to modify its
> arguments in place?
> *Most* of subroutine signatures are about clearer more efficient syntax for
> copying arguments and setting defaults - the use of "modify in place"
> arguments is really rare.
> On the other hand, if I misunderstood this, and you mean that the
> subroutine
> doesn't modify any part of complex data structures passed into it -
> enforcing this sort of deep readonly with the current implementation is
> pretty much a non-starter.

You may be right ... I tried to say "sub doesn't modify anything (except
its own state variables)"

> > and more derived from
> >
> My summary is that it feels like this syntax is part of much larger design,
> for a language *derived* from Perl. To take the "family" analogy often
> used,
> this plan is a picture of another "sibling" of Perl, not a picture of Perl
> later in life.
> (Except where I explicitly mentioned the PSC, the above is my opnion, not
> the PSC's. I don't what the other 2 (soon 3) members of the PSC think, and
> they may well disagree with me.)
> Nicholas Clark

Thread Previous | Thread Next Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at | Group listing | About