Mailing List Archive

RFC: define meta operator
Markdown expanded:
https://github.com/happy-barney/perl-poc/blob/perl-rfcs/RFC-define-meta-operator.md

Available as PR (for per-line comments)
https://github.com/happy-barney/perl-poc/pull/3/files

# Preamble

Author: barney@cpan.org

Title: Assign meta operator

# Abstract

Provide dedicated syntax for declaring meta properties of misc entities.

# Motivation

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](https://github.com/Ovid/Cor/)
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`

- 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)

# Specification

## General syntax

New chainable, left associative binary operator `LHS := meta property
expression`

## 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 (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

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).

## Meta properties API

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`

## Specify meta properties on package

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;
```

## Specify meta properties on subs

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 };
```

## Specify meta properties on variable

```
my $bar := :is => CORE::Number:: = 10;
$bar := :readonly;

has $var
:= :is => CORE::Number::
:= :is => CORE::Defined::
:= :default => -1
:= :readonly
:= :private
;
```

## Standard meta properties

### :extends => parent

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.

### :does => Role

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;
```

### :is => Constraint

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.

### :required

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
```

### :available

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;
}
```

### :default => value

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.

### :bind => another-object

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 { ... };
```

### :readonly

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

# Alternative syntax

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::
;
}
```

# Related work

- java annotations
- this RFC provides most of expressivity provided by java annotation
- in java one annotates data-type
- annotations cannot by specify dynamically

# Future work

## Trigger API

Specify callback what to do when property is set

## Perl API

## replace attributes

usage is dedicated operator is easier to read (and parse as well)

## named sub arguments, list sub arguments

```
foo $bar := 1, @baz := 2 .. 3;
```

and more derived from
https://github.com/happy-barney/perl-poc/tree/perl-features/COP
Re: RFC: define meta operator [ In reply to ]
On Sat, Jun 12, 2021 at 10:13 PM Branislav Zahradník <happy.barney@gmail.com>
wrote:

> Markdown expanded:
>
> https://github.com/happy-barney/perl-poc/blob/perl-rfcs/RFC-define-meta-operator.md
>
> Available as PR (for per-line comments)
> https://github.com/happy-barney/perl-poc/pull/3/files
>
> # Preamble
>
> Author: barney@cpan.org
>
> Title: Assign meta operator
>
> # Abstract
>
> Provide dedicated syntax for declaring meta properties of misc entities.
>
> # Motivation
>
> 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](https://github.com/Ovid/Cor/)
> 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`
>
> - 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)
>
> # Specification
>
> ## General syntax
>
> New chainable, left associative binary operator `LHS := meta property expression`
>
> ## 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 (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
>
> 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).
>
> ## Meta properties API
>
> 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`
>
> ## Specify meta properties on package
>
> 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;
> ```
>
> ## Specify meta properties on subs
>
> 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 };
> ```
>
> ## Specify meta properties on variable
>
> ```
> my $bar := :is => CORE::Number:: = 10;
> $bar := :readonly;
>
> has $var
> := :is => CORE::Number::
> := :is => CORE::Defined::
> := :default => -1
> := :readonly
> := :private
> ;
> ```
>
> ## Standard meta properties
>
> ### :extends => parent
>
> 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.
>
> ### :does => Role
>
> 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;
> ```
>
> ### :is => Constraint
>
> 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.
>
> ### :required
>
> 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
> ```
>
> ### :available
>
> 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;
> }
> ```
>
> ### :default => value
>
> 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.
>
> ### :bind => another-object
>
> 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 { ... };
> ```
>
> ### :readonly
>
> 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
>
> # Alternative syntax
>
> 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::
> ;
> }
> ```
>
> # Related work
>
> - java annotations
> - this RFC provides most of expressivity provided by java annotation
> - in java one annotates data-type
> - annotations cannot by specify dynamically
>
> # Future work
>
> ## Trigger API
>
> Specify callback what to do when property is set
>
> ## Perl API
>
> ## replace attributes
>
> usage is dedicated operator is easier to read (and parse as well)
>
> ## named sub arguments, list sub arguments
>
> ```
> foo $bar := 1, @baz := 2 .. 3;
> ```
>
> and more derived from https://github.com/happy-barney/perl-poc/tree/perl- <https://github.com/happy-barney/perl-poc/tree/perl-features/COP>features <https://github.com/happy-barney/perl-poc/tree/perl-features/COP>/COP <https://github.com/happy-barney/perl-poc/tree/perl-features/COP>
>
>
Frankly this looks to me like it isn't Perl anymore. None of it is
intuitive to me.

Leon
Re: RFC: define meta operator [ In reply to ]
On Sat, 12 Jun 2021 at 22:44, Leon Timmermans <fawaka@gmail.com> wrote:

>
> Frankly this looks to me like it isn't Perl anymore. None of it is
> intuitive to me.
>
> Leon
>

ad "Perl anymore"

What is in fact Perl ?

True, this is RFC is not Perl, it is a proposal to improve it.
Proposed syntax is chosen

- to be readable
It let important parts (symbol name and what kind of symbol it is) always
be at same place followed by optional parts.
So when you look at line (or grep) you will always see "sub X", "package
Y", "my/local/has $z" first.
When you read a code, you navigates through symbol. It should express
intention why it exists.
When you expect fulfilled intention, you don't care about implementation
- same reason people usually use provided `sort`
instead of implementing it.

For same reason RFC contains usage of `not` for boolean parameters.

- to be available syntax
usage of new operator is compromise between capabilities of current
grammar and new requirements.
Token ':=' cannot appear as valid syntax token in current code, so it can
have it's own rules (eg: reusing keyword `not`)
Same reason is for using ':' as a property name sigil (I didn't use word
adverb because LeoNerd use it quite often on IRC
so I'd rather avoid possible confusion at least here).

So syntax can be implemented using current perly.y and toke.c (one change
- fat comma has to be token on its own)

ad "not intuitive"

That's probably related to either my incapabilities to express myself as
well as lack of bigger picture - this RFC is first
of few more planned towards native support of context oriented programming
in perl.
Re: RFC: define meta operator [ In reply to ]
2021-6-13 5:13 Branislav Zahradník <happy.barney@gmail.com> wrote:

> Markdown expanded:
>
> https://github.com/happy-barney/perl-poc/blob/perl-rfcs/RFC-define-meta-operator.md
>
> Available as PR (for per-line comments)
> https://github.com/happy-barney/perl-poc/pull/3/files
>
>
Thank you for posting RFC.

On the other hand, looking at the currently planned RFC process, the first
process seems to be a conversation of ideas in p5p mailing list before
creating Draft RFC.

https://github.com/Perl/RFCs/blob/master/docs/process.md
Re: RFC: define meta operator [ In reply to ]
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.

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.

> Note 2:
> Few times I'll mention [Ovid's Cor proposal](https://github.com/Ovid/Cor/)
> 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.

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.

> 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.

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

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.

> `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
> ```

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.

> 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.

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.

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.

> 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.

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.

> 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;


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.

> ### :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.

> 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.

> and more derived from
> https://github.com/happy-barney/perl-poc/tree/perl-features/COP


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
Re: RFC: define meta operator [ In reply to ]
Hi Nicholas, thanks for comment

On Mon, 14 Jun 2021 at 12:38, Nicholas Clark <nick@ccl4.org> 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
cases
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](
> https://github.com/Ovid/Cor/)
> > 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
definition.
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
property.

Examples how it should look like

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

does:
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
more.

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.

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


>
> 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
call)

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:
https://github.com/apache/kafka/blob/trunk/core/src/main/java/kafka/metrics/KafkaYammerMetrics.java#L38

has $INSTANCE
:= :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
necessary.
(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
grammar.
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
`is`).

Definition of constraints should be stand-alone RFC and I consider this one
as
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
multidispatch
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
initialized
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
> > https://github.com/happy-barney/perl-poc/tree/perl-features/COP
>
>
> 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
>
Re: RFC: define meta operator [ In reply to ]
On Sat, Jun 12, 2021, at 4:12 PM, Branislav Zahradník wrote:
> Title: Assign meta operator

I have read this now.

I found it very difficult to evaluate, because I didn't feel there was, to me, a very clear statement of the specific problems to be solved, as opposed to the specific keywords that might be introduced. (Also, I'm not sure I saw how this related to what PL theory calls "context oriented programming". Possibly it is not meant to.)

The core of your proposal appears to be two things:

1. a new mechanism for attaching a new attribute-like property to data elements
2. some specific semantics for specifically named attributes

…but I think that, in isolation, it's just too abstract. "I want some new syntax to do something to solve a bunch of problems that are unified by some principle that has not been clearly stated in the proposal." This feels like it should be a semantics-forward proposal, but it's been presented much more syntax-forward.

Also, as a side note, don't forget the wisdom of Larry Wall:
> The first rule of computer language design is that everybody wants the colon.

You're shooting for some high-value syntax there. It's going to be a hard sell.

--
rjbs