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

Re: This is not an RFC to bring modern OO into the Perl core

Thread Previous | Thread Next
From:
mah.kitteh via perl5-porters
Date:
June 20, 2021 14:56
Subject:
Re: This is not an RFC to bring modern OO into the Perl core
Message ID:
2HbH35OeLMrecmzIdl7bt38bM_uLysRH4m2md_HTzCeNSOHEoV_l3_NXi0ec0xXktM-ixINkFVFFBzCD_e1_zfxML0zNuYjjsNM10wUiHxE=@protonmail.ch
+5 Insightful. Thank you, Chris. I was neither annoyed, nor offended. I also read it all (lest I get accused of not reading or thinking, which happens quite often).

FWIW, I have not done much thinking about what an object system looks, mnuch less in in perl. It's not necessarily that I personally don't see the need or use OOP beyond providing convenient dereference syntax, it's that nothing I have seen seems at all complementary to Perl or perl. Util::H2O is a good example of what it means to be complementary or to work within the framework established by Perl/perl. It also seems quite "counter-cultural" if you ask me. Is it good on large scale code? Idk, I am going to guess probably not without sufficient additional abstractions - so I am not advocating this as the solution, only a good and interesting option for some.

Ultimately, my point is this: whatever is done to support things like "class", "private", etc should be natural extensions and complements to the current Perl "OO" syntax (superficially at the very least). This is important to me for two reasons: 1) I want to be able to reach for it if I need it doing what I already know (package, bless, deref syntax); and 2) I want to feel confident that supporting such syntax is not bolting on a "parallel" system to the perl core runtime to support these things when the existing can be simultaneously improved and extended to support such things.

A corollory to #1 is that I want to be able to jump into maintenance of a code written using Corinna (or whatever is Perl's OOP-ng) and be immediately familiar with it knowing what I already know: a) about Perl/perl, and b) about OOP in general. I can't do that with Moo/Moose and in fact have gotten to the point where I simply refuse to do so because the code base it generates is so abjectly foreign to me that I can't even consider it Perl.

And this gets the my preference for a "bottom up" versus "top down" approach to this. Given what we have now, how do we extend things to get to where we (think) we wish to go? Rather than saying we want $THIS, how do we work back? Can we go from status quo to Corinna as it is specified now, incrementally and naturally? I pointed out Util::H2O at the start because it is the result of such thinking. (and for that matter, regarding another topic, so is Sub::Infix) - how far can we get with what we have _now_? What fundamental capabilities of core are require to easily support such things? How can we flush out new interesting things and also minimally extend core, etc?

Cheers,
Brett

‐‐‐‐‐‐‐ Original Message ‐‐‐‐‐‐‐

On Saturday, June 19th, 2021 at 11:51 PM, Chris Prather <chris@prather.org> wrote:

> This is probably going to annoy some of you. If you want something
>
> that is concise, relevant, and technical ... don't read this email.
>
> It's any one of the three, but never all three together.
>
> On Sat, Jun 19, 2021 at 9:41 PM mah.kitteh via perl5-porters
>
> perl5-porters@perl.org wrote:
>
> > On Saturday, June 19th, 2021 at 3:44 PM, Darren Duncan darren@darrenduncan.net wrote:
> >
> > > Using "modern" to describe something is so over-used its lost all meaning.
> >
> > I don't think "post-modern" is helpful either. I much prefer the term "counter culutural".
>
> Wikipedia says "A counterculture is a culture whose values and norms
>
> of behavior differ substantially from those of mainstream society,
>
> sometimes diametrically opposed to mainstream cultural mores."[1] so
>
> obviously Corrinna cannot possibly be counter-cultural. Her norms of
>
> behaivor do not differ substantially from those of either mainstream
>
> Perl nor mainstream Object Oriented Programming. This was in fact
>
> the original complaint about the term "modern".
>
> Second, Corinna is patently not postmodern either. As has been
>
> discussed before[2], postmodernity has three basic parts in its
>
> definition, Corinna only matches one of them— “of, relating to, or
>
> being an era after a modern one”. It is not a return to traditional
>
> mediums and forms, it is also not an ironically self referential
>
> commentary on the status quo. Neither is it entirely a radical
>
> reappraisal of modern assumptions about culture, identity, history, or
>
> language, in so far as I don't think anything about it is at this
>
> point radical for anyone who's worked with an object oriented language
>
> developed after the year 2006.
>
> Corinna is a post-postmodern object system—though possibly not the
>
> first one, Raku probably has a claim to that title. Corinna oscillates
>
> between modernist positions regarding Object Oriented Design, and the
>
> postmodern deconstructionist principles that were embraced
>
> dramatically by Moose. Corinna is a metamodern[3] object system. "How
>
> on earth is this relevant?" I imagine you asked long before you got
>
> to this part of the email. It speaks to why the approach Brett is
>
> advocating simply won't work (beyond the fact that people have been
>
> trying it for several years now).
>
> > Rather than peeling the OOP "onion" back layer by layer, build it out from what exists now. Starting with what's needed to augment "bless", prototypes, and [overload.pm].
>
> Without trying to sound offensive, this list kinda suggests you've not
>
> really done any extensive thought about what an object system is and
>
> should be. Most people don't and shouldn't ever need to. A list of
>
> things that in my opinion would need enhancement:
>
> -   Classes: Perl just gives you packages with a special @ISA variable
>
>     for inheritance. Packages are just a bag of subroutines, they have no
>
>     idea of state.
> -   Attributes: `bless` associates a package with a data structure to
>
>     provide "attributes", except it doesn't actually provide attributes,
>
>     it just provides a place to store data and leaves you to figure out
>
>     what attributes are and what that means. This also means that all
>
>     instance data is public by default. While we pretend that it doesn't
>
>     because Larry told us not to play with shotguns, it hasn't stopped a
>
>     lot of people putting shotgun like things onto CPAN (or into Perl Best
>
>     Practices).
> -   Metamodel: The way you interrogate and manipulate a package is ....
>
>     not obvious. Package::Stash exists on CPAN simply to provide an API
>
>     for this manipulation because it's fraught with edge cases and weird
>
>     syntax.
> -   Methods: Perl's concept of a method is a subroutine called in a
>
>     funky way. Combined with the public nature of the data, this means you
>
>     can call any method on any object ... and the only thing that can
>
>     prevent this is the method itself. I've never seen anyone write enough
>
>     validation code at the beginning of their methods to deal with what is
>
>     actually possible to throw at a method.
> -   Class composition: Design Patterns: Elements of Reusable
>
>     Object-Oriented Software, published literally 4 days after Perl 5.000
>
>     says to prefer composition to inheritance. Perl's only solution to
>
>     reusable behavior is inheritance. Worse, Perl supports multiple
>
>     inheritance using a default algorithm that can cause weird non-obvious
>
>     bugs.
> -   Object Construction Protocol: Ensuring that all of the attributes
>
>     are initialized properly in the correct data structure during
>
>     construction is left entirely as a lemma for the programmer.
> -   Object Destruction Protocol: See above, but because Perl has
>
>     universal destruction where we can't even guarantee the order in which
>
>     things are destroyed.
>
>     The fact that Perl's built in object system just gives you a bag of
>
>     primitives and leaves you to build a robust object system for every
>
>     application you write is kinda the reason things like Moose exist.
>
>     Moose's choices to solve many of these problems is the reason Corinna
>
>     exists. Let's take Classes, attributes, and methods for example
>
>     (because this is the most obvious change in Corinna). Classes are
>
>     supposed to be a template for creating objects with initial
>
>     definitions of state and implementations of behavior. Perl's native
>
>     system only provides the second half of that.
>
>     package Player;
>     use 5.34.0;
>     use warnings;
>     use experimental 'signatures';
>
>     sub new($class, $data) {
>         # a "character" property is required so we have to check here
>        die "Must provide a character object" unless $data->{character};
>
>
>         #  every player must have a name, but we can provide a default
>         $data->{name} //= do { state $n = 1; 'Player' . $n++ };
>
>
>         bless $data, $class;
>     }
>
>     # and we have to write our own accessor if we don't want people to
>     just use $self->{name}
>
>     sub name($self) { $self->{$name}  }
>
>
>     sub attack($self, $target) { print STDERR "$self->{name} attacks
>
>     $target->{name}"; ... }
>
>
>     1;
>     ```
>     Moose provides a template for state but the way it provides it
>     encourages a proliferation of behavior;
>     ```
>     package Player;
>     use 5.34;
>     use Moose;
>     use experimental 'signatures';
>
>     has name => ( is => 'ro', default => sub { state $n = 1; 'Player' . $n++ } );
>
>     has character => ( is => 'ro', required => 1 );
>
>
>     __PACKAGE__->meta->make_immutable;
>
>     ```
>     This has templates for the state, but now our character object has a
>     public accessor. Moose can be told not to generate an accessor.
>     ```
>     package Player;
>     use 5.34;
>     use Moose;
>     use experimental 'signatures';
>
>     has name => ( is => 'ro', default => sub { state $n = 1; 'Player' . $n++ } );
>
>     has character => ( is => 'bare', required => 1 );
>
>
>     sub attack($self, $target) { print STDERR $self->name() . " attacks "
>
>     . $target->name(); ... }
>
>     __PACKAGE__->meta->make_immutable;
>
>     ```
>     But now the only way to access the character attribute is to look in
>     $self->{character} which poses a problem. Moose tries to hide the fact
>
>     that all objects are blessed hashes, they are, everyone knows they
>     are, but it's considered bad form to treat them as such because doing
>     that you throw away any advantages Moose can provide to attribute
>     access. We can fix that as well
>     ```
>     package Player;
>     use 5.34;
>     use Moose;
>     use experimental 'signatures';
>
>     has name => ( is => 'ro', default => sub { state $n = 1; 'Player' . $n++ } );
>
>     has character => (
>
>         is => 'bare',
>
>         reader => '_character',
>
>         required => 1
>
>     );
>
>     sub attack($self, $target) { print STDERR $self->name() . " attacks
>
>     $target->{name}"; ... }
>
>
>     __PACKAGE__->meta->make_immutable;
>
>     ```
>     Now you have a public method that uses the Perl convention of
>     underscore prefix means it's private so pretend you don't see it. Oh
>     and to make all of this operate at a reasonable speed you have to
>     include __PACKAGE__->meta->make_immutable; at the end of every package
>
>     so that Moose spends a lot of compile time generating and eval-ing
>     strings into packages so that at runtime you get almost the same speed
>     as an equivalent native method.
>
>     From a design perspective this means that there is a lot of state and
>     behavior you either simply pretend aren't public or actively work
>     around Moose to encapsulate. Corinna's approach to this is to have
>     lexically scoped member variables that aren't accessible outside the
>     class.
>     ```
>     use strict;
>     use warnings;
>     use feature 'class'; # bring in Corinna
>
>     class Player {
>         has $name :new :reader = do { state $n = 1; 'Player' . $n++ };
>         has $character :new;
>
>         method attack($target) { say STDERR "$name attacks " .
>     $target->name(); ... }
>
>     }
>     ```
>     This default is changed from public first, to private first. The only
>     subroutines in the class are the ones you explicitly ask for (:reader)
>     or define (method). Because Corinna is baked into core there is no
>     need to inject strings into packages at compile time to be parsed so
>     that everything runs at native speed. The augmentation kind of
>     requires the entire re-thinking of how classes are implemented in
>     Perl. Oh! And the pieces are hard to tease apart, because once you
>     start thinking about attributes as an essential part of a Class you
>     now have to have an Object Construction Protocol to make sure they're
>     initialized. An Object Construction Protocol is a part of a well
>     defined metamodel. If you want to encapsulate data into a lexical
>     scope you need methods that can access that lexical scope (and can't
>     access a different classes scope). So the augmentation starts hitting
>     several parts that Perl currently doesn't have ... just to get the
>     basic behavior.
>
>     You've slogged through all of this ... but yeah there's one more thing
>     I wanted to point out. You could reduce Corinna by getting rid of the
>     slot attributes like ':new' and ':reader'. All of the features we were
>     looking for with private attributes are still there, the code is just
>     more verbose.
>     ```
>     use strict;
>     use warmings;
>     use feature 'simpler_class'; # a simpler Corinna
>
>     class Player {
>        has $_name = do { state $n = 1; 'Player' . $n++ };
>        has $_character;
>
>        method new($character, $name=undef) {
>           $_character = $character;
>           $_name  =  $name if defined $name;
>           return $self;
>        }
>        method name() { $_name }
>        method attack($target) { say STDERR "$name attacks " . $target->name(); ... }
>
>     }
>     ```
>
>     So where were we? Oh right! Corinna is a metamodern object system
>     because it rejects the postmodern concept of deconstructing the
>     existing object system (by rebuilding it with a robust framework) and
>     instead returns to the modernist design of extending the core language
>     with the attributes we found most appealing after gazing at the system
>     with our postmodernist mindset, but without the baggage that the
>     postmodernist ironic self-reflexive implementation required us to
>     have.
>
>     Corinna is metamodern.
>
>     Thank you for coming to my Ted Talk.
>
>     [1]: https://en.wikipedia.org/wiki/Counterculture
>     [2]: https://chris.prather.org/why-moose-is-post-modern.html
>     [3]: https://en.wikipedia.org/wiki/Metamodernism#Vermeulen_and_van_den_Akker
>

Thread Previous | Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About