Perl-Apollo / Corinna

Corinna - Bring Modern OO to the Core of Perl
Artistic License 2.0
157 stars 19 forks source link

Methods Feedback #15

Closed Ovid closed 3 years ago

Ovid commented 4 years ago

Please leave comments on Methods here.

haarg commented 4 years ago

How is private method dispatch expected to work? Would it be using the equivalent of a caller check? How would this apply to $object->$method or $object->can($method)?

In an overridden method, how would you call the parent class's method? next::method?

haarg commented 4 years ago

The pseudo-grammar is missing the method name.

Would my $foo = method { ... }; or my method foo { ... } work?

haarg commented 4 years ago

Multi-methods (just like signature enhancements) shouldn't apply to only methods. If they are implemented, they should apply equally to all subs. So I wouldn't consider them as part of the Cor proposal.

Ovid commented 4 years ago

@haarg Regarding multi-methods, I agree. But since my work is for Cor and not the general language, I'm kinda stuck there. I don't know what the right answer is.

Ovid commented 4 years ago

@haarg: I've also tried a first-pass address of the missing method name issue. A signature is actually the method name plus the arguments.

HaraldJoerg commented 4 years ago

I suggest to explicitly mention one of the important properties of methods: The instance is available as the lexical variable $self. Other languages use other names for this variable, and the reason for Perl's choice (writing my $self for 'myself') is no longer apparent in Cor methods.

About multi methods - if there ever is some conflict which feature should not be in the first minimum viable product, then multi methods would be a good candidate to drop. They may be a cool feature, but they aren't that useful without a type system.

Some remarks about your examples:

About authentication: what if you want authenticate($resource,$token)? I've been developing an authentication system for some years, and during that period, thanks to technical progress elsewhere, it saw two different token-based authentication schemes. The lesson here is: multi methods are tricky to extend. We don't want to see multi method authenticate($resource,$token,$junk,$junk) "I need two pieces of junk since the three-argument-version has already been taken". Cor takes the position you want your contract to be as small as possible is a rather eristic statement if the complexity is just shifted from method names to different lengths of argument lists.

The quicksort example makes traditional Perl look worse than it needs to be. Look at this:

sub quacksort (@list) {
    2 > @list  and  return @list;

    my $pivot = shift @list;
    return (
        quacksort( grep { $_ < $pivot } @list ),
        $pivot,
        quacksort( grep { $_ >= $pivot } @list ),
    );
}

You can't shortcut lists with 0 and 1 elements like this in a multi method. Also, there's an exercise: Rewrite quicksort so that the last element is taken as the pivot. The lesson is: this multi method example restricts options for implementation.

In my own Java practice, the most frequent use of multi methods was for parameters which could take different equivalent forms - an object, a key/value hashmap, or a serialisation thereof. Perl / CPAN has many of these cases where elements of @_ are examined whether they are references. Cor's multi methods won't help here.

So while I don't want to prevent Cor's multi methods from happening, I can live pretty well without them.

haarg commented 4 years ago

@haarg Regarding multi-methods, I agree. But since my work is for Cor and not the general language, I'm kinda stuck there. I don't know what the right answer is.

Multi-methods (and signatures) are a general language feature. If you want them, create a separate proposal for them. You can work on two independent proposals at the same time.

HaraldJoerg commented 4 years ago

Some thoughts about keywords:

Yeah... I can imaging that parsing class method isa {...;} vs. class method isa does {...;}is tricky.

duncand commented 3 years ago

It would be best to exclude multi-methods. These are a design smell in principle, and any case where one would be thinking of using them, they should instead be using normal different-named methods.

duncand commented 3 years ago

If you're going to support private, rather than everything being public, then you should also support trusts like Raku has. Analogies in other languages are internal scope of .NET/C# and package-private scope of Java (but this is broken), but the .NET concept is based on the concept of a common assembly which doesn't exist in Perl. Raku's example is what you want to follow. I agree that protected isn't needed. Trusts is the best medium between private and public. Trusts or analogy is also necessary for many projects. Also one needs to be able to declare not only regular methods but also constructors and accessors such that certain other classes can call them but not the general public. For example, a class that should only be created by a class factory should say its constructor trusts the class factory class and is otherwise private.

duncand commented 3 years ago

I propose simplifying things by omitting the whole concept of class methods, and make all methods instance methods that have a $self automatically in scope.

Generally the whole concept of a class method in other OO languages like Java is an artifact of their design that every package is a class and every routine is a method. But Perl already has non-object packages and subroutines which can satisfy that.

Unless you are forbidding things in classes that classic Perl packages support such as regular subs or $my things declared within the package/class scope, you can use those things for certain non-instance stuff.

What I propose is that the only way one is allowed to invoke a method is on an object, and they mot invoke a method directly on a class name, and every method has a $self. Fundamentally instantiating an object would have a special or factory-like syntax built-in to the language which is the only place the class name would appear for that purpose.

From a design point of view, even classes for whom you only expect to have a single instance within a running program are better implemented as object classes where you use an object to mediate everything it controls rather than a static class you don't instantiate. This is true in Java/etc and it would be true in Perl.

Where logically you must not have multiple instances or a singleton, you just accomplish this by not having any instance-specific slots and all slots are declared shared, or declared in class/package scope with my.

All construction methods in a class would have a $self with all the slots already available and with appropriate type-specific default values, which is undef by default.

Factories are just ordinary classes you first instantiate or select an object of and then use a method on that to create the object you actually want, or alternately Factories are non-object packages with normal subs.

So this normalizes the Perl code including that all methods are called with the same syntax and all methods reliably have a $self.

duncand commented 3 years ago

Following my prior comment, this change should benefit users of otherwise static classes or packages making things more terse without relying on exporting/importing.

Instead of this:

use FooBarBaz;
FooBarBaz::a();
FooBarBaz::b();

You can have something like this:

use FooBarBaz;
my $fbb = select FooBarBaz();
$fbb->a();
$fbb->b();

This is also a good basic design in classic Perl 5 to normalize all code on OO syntax even when conceptually you don't have object instances.

This way you consistently use -> for calls, avoiding problems with ::, and you avoid repeating possibly long package names everywhere and without using importing/exporting.

Ovid commented 3 years ago

This is resolved for the MVP. Further issues should be new tickets.