A tool to make it easier to manage multiple code repositories using different VCSen
genehack/HiD 28
Static website generation system *NOTE:* Main dev mirror at https://git.genehack.net/genehack/hid
Perl wrapper for git(7) command line interface
emacs configuration files and libraries. Migrated to https://git.genehack.net/genehack/emacs
MIGRATED TO https://git.genehack.net/genehack/jaquard -- social network/RSS client
MIGRATED TO https://git.genehack.net/genehack/app-status-skein - Stream-style Twitter/Facebook/Identi.ca aggregation, posting, and other interaction
genehack/bullshit-contributions 4
Github contributions are bullshit. This repo corrects that. (For me, at least.)
MIGRATED TO https://git.genehack.net/genehack/app-mise-en-place
MIGRATED TO https://git.genehack.net/genehack/build-color - handy script and code to build 'color' binary
genehack/dist-zilla-pluginbundle-genehack 3
MIGRATED TO https://git.genehack.net/genehack/dist-zilla-pluginbundle-genehack - BeLike::GENEHACK when you zilla your dist
created repositoryOpentrons/oe-core
The heart of Opentrons' openembedded system definitionsa
created time in a day
startedargoproj/argocd-example-apps
started time in 3 days
startedJacquesLucke/animation_nodes
started time in 4 days
startednerk987/RigFlex
started time in 4 days
startedSebOuellette/LiveBot
started time in 6 days
issue openedstevan/p5-MOP
Consider : dumbing down the roles and slots pragmas + improving the *DOES / *HAS conventions
Hi Stevan,
I am posting this on the MOP repo - lack of a better place for these kind of general topics.
It started out as an enhancement request for the roles pragma and ended up becoming a general note about the MOP and the slots roles pragmas,
as well as the @DOES and %HAS conventions (suggesting 2 similar symmetrical conventions along the way: @HAS and %DOES).
It's quite loooong. And it might contain a high amount of gibberish. My apologies if that's the case.
The @DOES and %HAS conventions ..
Having a mechanism through which a package (role, class, whatever) can simply state the roles it wishes to do, without bothering with the details of how they are composed, is very interesting.
And one way of achieving that is to stuff those wishes in an array (like @DOES), in a way akin to @ISA.
Same goes for the ability to declare a slot by just putting some stuff in a package variable %HAS (or @HAS, see below).
I think this an excellent idea... And I hope to convince you to stop seeing these as just "surface features" of the new MOP and the slots / roles pragmas, as mentioned in 4.
If well specified/documented, those package variables have got the potential to become a glue/wormhole between the living quarters of Flintstones and the Jettsons and anything in between...
-- as simple conventions that can be followed by any current or future MOP / role-composer, including the popular Role::Tiny, the Cor project, and perhaps even the Mo* family at some point...
That sort of thing is well served being "low-tech"... And that's the beauty!
The fact that roles are not currently implemented in core is mostly irrelevant, I believe. So is the "hijacking" of the package symbols --which can be a problem, yes... but not in any way different than what it would be if it were done by core...
There do appear to be a few hurdles on that pathway, though...
The case of @ISA
-
- The good old
@ISAis normally pure data;
- The good old
-
- It's where a package/class declares its immediate ascendants (immediate == "local", in MOP parlance), which conceptually correspond to what a class wishes to descend from, nothing more.
-
@ISAdoesn't itself contain any "dirty state" of the inheritance operation or method dispatch. Caching and other dirt occurs elsewhere.
-
- There are a gzillian ways to populate
@ISA...use parentbeing the preferred one these days... Normally, it doesn't matter who populated it or how, as long as it is there when it is needed (method dispatch).
- There are a gzillian ways to populate
-
- The code that populates
@ISAis not typically the code that implements any form of inheritance or method dispatch. Thebasepragma partially violated this (by entangling itself with%fieldsand pseudo-hashes). Remember what happened later?
- The code that populates
What troubles me with roles, slots and the MOP
In a nutshell, I think the roles and slots pragmas are doing too much and the MOP commits a sin by reading from and writing to the same place, namely %HAS :-)
-
- The
rolespragma does too much because it combines (and tightly couples) the declaration and composition of roles. This is somewhat akin to what plagued thebasepragma over time.
- The
-
- A similar situation applies to the
slotspragma, which goes beyond simply stuffing the caller's wishes somewhere, but actually schedules slot inheritance.
- A similar situation applies to the
-
-
Also, both the
rolesslotspragmas (well, actually theMOP) commit a sin by stuffing the dirty state (i.e. one of the outcomes) of role composition within the%HAShash (which also serves as theirINPUT)I am not sure if there is an existing module that commits an equivalent "sin" for
@ISA. The analogy would be something likemromodule rewriting@ISAatUNITCHECKtime and stuffing the equivalent ofget_linear_isa()in there...
-
Suggested evolution
Here's an alternative way of dealing with the above (which you might have already considered and ditched; if so, I would really like to hear the reasoning), which entails hijacking 4 package variables (instead of 2) though:
- wishes in
@HAS==> merged to%HAS(during successfull composition) - wishes in
@DOES==> merged to%DOES(during successfull composition)
This may sound complex, but I think it actually results in something simpler, and does away with some of the circularities, both conceptually and implementation-wise. Just bare with me, please.
-
The array variables (
@HASand@DOES) would be where "local" wishes are recorded by mere mortals (or thru dumbed down versions of theslotsandrolespragmas, as described below), meaning ==>@DOESwould keep its current semantics. -
The hash variables (
%HASand%DOES) would be where the related claims are placed, typically merged in via role composition (but not necessarily), meaning ==>%HASwould keep its current semantics.The suggested
%DOESconvention (where role claims would be placed) is more of a nice-to-have, but its presence provides a symmetrical way to present things, also absorbing the gist ofClass::DOESalong the way. -
The
slotsandrolespragmas become even simpler than they already are, they would just be stuffing things in@HAS/@DOESrespectively (similar to what theparentspragma does to@ISA), without doing/scheduling any composition. -
A conforming composer (like
MOPand possiblyRole::TinyandObject::Pad) would just look at@HASand@DOESin order to gather the relevant requests, then do their thing, and then merge the resulting successfull claims in%HASand%DOES.
Any other conforming composer (like
Role::Tiny, orObject::Pad, if they wish to conform) would also be able to merge things in%HASor%DOES. This could also include mere mortals (at their own risk)
- The
DOES()method becomes trivial (wherever it is implemented): it would only have to look at%DOESand just deal with the@ISAinterplay. It would need no services from theMOP(except perhaps a utility function for gentle stash access, currently inMOP::Internal::Util).
And the whole thing is pretty much compatible with the current state of affairs (in terms of API) and requires minimal to no changes to UNIVERSAL::Object (the more stable sister of the bunch), depending on the way you look at it.
It comes with a small nuisance though:
-
Participating classes and roles (those adding slots or wishing to do other roles) would need to
usean additional module, which I nicknamedComposerbelow (just made up the name, it's probably not the best).Composer, also quite a brief, is where role/slot composition would be scheduled (instead of from within theslotsandrolespragmas). If/when the MOP makes it tocoreit may become a no-op.
What's the point ?
What do we gain from this nonsense?
In terms of direct functionality gains: not much -- except for a future possibility to reconstruct slot definition order via @HAS, which may become handy at one point.
The main advantages come from a clear separation of concepts and concerns, and a potential for interop going forward.
In this paradigm:
-
The
@HASand@DOESconventions can be independently specified/documented, focusing only on semantics. They become true declarations of "local" wishes / requests. This is very similar to the case of@ISA. -
The dumbed down versions of
slotsandrolespragmas can be used anywhere without any implied entanglement with a any given implementation, just like theparentpragma. They also become the perfect places to document and maintain the@HASand@DOESconventions. You also get quasi immediate and solid behavioral stability for theslotsandrolespragmas. They become so dumb and boring so as to deserve v1.0 on a fast-track. -- otherwise, people would possibly sue them in the future for name-squatting :-) -
The
%HASand%DOESvariables are where things are reported / claimed (as opposed to simply being requested). For example, in addition to "local" slots coming from@HAS, the inherited/composed slots also find their way into%HAS. Likewise, the%DOEShash would contain all the roles done by a given package (including those composed through roles that do other roles), but not necessarily those done by its ancestors. -
With these small adjustments, it becomes very easy to swap composers at will (and experiment with new ones) without reinventing too many wheels and new conventions.
-
The
MOPremains to be a passive light-weight toolbox/library with no state of its own (like today). -
A
Composeris an active thingy: it takes initiative (ifused). It uses aMOPto do its thing. Going forward, there may be different implementations for each. -
The standard
Composer(or alternate composers) do not really need to expose much of an API distinct from what is described. Almost no one really needs to call upon their specialized methods:
- They get their INPUT from known places (
@HASand@DOES) with documented semantics. - They do their thing when they see fit (thanks to your phase-scheduling code)
- After doing their thing, they just spit out their OUTPUT (report) to other known/documented places (
%HASand%DOES)
This should also make it easier to include this stuff into the core. Just one hook on UNITCHECK.
What do we lose?
Possibly, some run-time meta-dynamism?... Not sure.
In any case, if we want that, we need to make sure that a successful compose_roles() operation can be repeated without issues .
CODE
Enough chatter. Here's some CODE, which should be much clearer than the long description above. (just to show intent. didn't even check if these compile)
Example usage
package Point;
use Composer; # May become a no-op if MOP makes it in core.
use parent qw/UNIVERSAL::Object/;
use roles qw(Geometric);
use slots (
x => sub {0},
y => sub {0}
);
package Point3D;
use Composer::Some::Alternative; # Allow alternative composers and experimentation.
use parent -norequire, qw/Point/;
use slots (
z => sub {0},
);
The slots pragma
The slots pragma becomes, in essence:
package slots;
sub import {
shift;
my $pkg = caller(0);
{
no strict 'refs';
push @{"$pkg\::HAS"}, @_;
}
}
The roles pragma
The roles pragma itself would become pure, void of any knowledge of how/when or even by whom a role will eventually get composed.
In essence:
package roles;
use Module::Runtime ();
sub import {
shift;
my $pkg = caller(0);
Module::Runtime::use_package_optimistically( $_ ) for @_;
{
no strict 'refs';
push @{"$pkg\::DOES"}, @_;
}
}
The DOES() method
The default DOES() method becomes (wherever it is implemented) :
sub DOES {
my ($self, $role) = @_;
# get the class ...
my $class = ref $self || $self;
# if we inherit from this, we are good ...
return 1 if $class->isa( $role );
# The suggested %DOES convention.
# This is not absolutely necessary, but just makes things
# simpler and potentially interopable between different MOPs.
# - TABULON
{
no strict 'refs';
require mro;
for my $pkg ( $class, mro::get_linear_isa() ) {
# Not exactly sure about the interpretation of a false value for a role.
# The below interprets it as an explicit claim for NOT doing a ROLE.
# FIXME::may need to access more gently or just go thru the MOP.
return ${"$pkg\::DOES"}{ $role } // 1 if exists ${"$pkg\::DOES"}{ $role };
}
}
return 0;
}
Note that, in terms of behavior, the above %DOES convention is almost identical and should be interoperable with Class::DOES. The only difference is the interpretation of falsy values in the %DOES hash:
- What I suggest above provides an easy way for a thingy to claim that it does NOT do a given role (maybe useful for debugging or what not). Not sure about this, though.
The Composer module
This is where things are actively tied together, but it's also dead simple (thanks to the MOP doing all the hard work).
package Composer;
use MOP ();
sub import {
shift;
my $pkg = caller(0);
MOP::Util::defer_until_UNITCHECK(sub {
my $meta = MOP::Util::get_meta( $pkg );
MOP::Util::inherit_slots( $meta ); # would we still need this line ?
MOP::Util::compose_roles( $meta );
});
}
OPEN QUESTIONS / ISSUES
Accept OptList as arguments for slots and roles pragmas ?
The slots pragma already accepts key-value pairs. It might be interesting to consider accepting an OptList as well (in the Data::OptList sense, but not necessarily depending on it).
A similar consideration applies to the roles pragma which currently accepts a flat list of role names. An upgrade to OptList looks like a natural fit there, once we start defining (or better yet, stealing) a sub-syntax for certain options that may be needed for resolving conflicts during role composition (exclusion, renaming, aliasing, ...).
I am not that sure about the form of the actual contents of @HAS and @DOES, though...
Guard against multiple COMPOSERS stepping on each others toes
Basically we would want to avoid alternate composers from stepping on each others toes, like in the case below :
use Composer;
use Composer::Other;
use slots ...;
use roles ...;
It might be possible to tackle this by having a Composer implementation mark its name within yet another package hash.... or export a subroutine or something... Need to think this through.
In any case, it smells like a stairway to... METACLASS()... :-)
...
There are some other open questions and points worth raising. But this post is already veeeery long...
created time in 7 days
startedgenehack/HiD
started time in 9 days
startedMESAInternational/B2MML-BatchML
started time in 9 days
startedlmc297/bactaxR
started time in 12 days
startedgrpc-ecosystem/grpc-gateway
started time in 13 days
created repositoryLaconicPneumonic/AudioVisualizer
Experimenting w/ three js to visualize audio
created time in 13 days
startedfuture-architect/vuls
started time in 15 days
startedzhaofengli/unmatched-nixos
started time in 18 days
created repositoryyanick/dungeon-of-doom
Toy app showcasing svelte-kit and rot.js
created time in 18 days
startedmicrobiome/mia
started time in 19 days
starteddrom/awesome-riscv
started time in 20 days
fork yanick/svench
A lightweight workbench to develop your Svelte components in isolation
fork in 20 days
startedcarlosedp/riscv-bringup
started time in 21 days
startedZymo-Research/figaro
started time in 22 days
startedpluja/awesome-privacy
started time in 22 days
fork ingydotnet/jekyll-anchor-headings
A GitHub Pages compatible way of adding anchors to your headings without a plug-in or JavaScript :octocat:
https://pure-liquid.allejo.org/
fork in 22 days
startedloft-sh/vcluster
started time in 25 days
A static site for the Jenkins automation server
fork in a month
fork jpcima/smart-tab
MIGRATED TO https://git.genehack.net/genehack/smart-tab - An intelligent tab completion function for Emacs
http://www.emacswiki.org/emacs/TabCompletion
fork in a month