Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses
 
PerlMonks  

Recap: The Future of Perl 5

by Ovid (Cardinal)
on Aug 23, 2018 at 13:03 UTC ( [id://1220917]=perlmeditation: print w/replies, xml ) Need Help??

Recently in The Future of Perl 5, there was a discussion of my keynote at the Glasgow Perl Conference. Rather than wade into that thread, I thought I should provide a brief explanation of what I was trying to achieve: a modest proposal (er, pardon that description) that was achievable, still Perl, and would not only return the conciseness of the language but would also allow Perl 5 to leapfrog many popular dynamic languages in terms of functionality (I'll skip the Perl 6 discussion and the inline discussion). This isn't a large-scale strategy to "fix everything", but a couple of small (ha!) steps which would make Perl much easier to write and maintain in large companies.


Perl has a long, strong history. Unfortunately, because Perl was the trail-blazer for many techniques taken for granted today, it sometimes went down dead-ends and those who followed learned from Perl. Perl also stole much from other languages and it's time to steal a bit more. Consider, for example, the lowly Fibonacci function. It would be natural to write it like this:

sub fib { my $nth = shift; return $nth if $nth <= 1; return fib( $nth - 1 ) + fib( $nth - 2 ); }

That looks like a perfectly natural way to write that function and if you pass it expected values, it returns expected results (we'll ignore the performance and memory issues as those aren't unique to Perl 5). But what if you pass it a -2? Or an undefined value? Or a string? Or a float? Or a typeglob? Or, or, or ...

At https://allaroundtheworld.fr/, we're used to working with large clients and large codebases and that sort of coding trips us up many times. It's hard building a large system, slinging data around left and write (er, "right"), without hitting these problems. In terms of maintenance, it's a nightmare. For short scripts, it's ok. When you're trying to build large-scale systems, it's a constant drag on developer time, even with tests (because you have to spend time and money to write tests to verify you've correctly handled what the language itself handles in many other languages). To write a robust Fibonacci function, you might write something like this:

sub fib { croak("…") if @_ != 1; my $nth = $_[0]; croak("…") if not defined $nth; croak("…") if $nth !~ /^+?\d+$/a; return $nth if $nth <= 1; return fib( $nth - 1 ) + fib( $nth - 2 ); }

But let's be honest: almost nobody does that and if you did, you'd be spending more time and money on something you often don't have to spend time and money on in other languages.

At a first pass, merely having optional typing in signatures would dramatically improve this:

sub fib(UInt $nth) { return $nth if $nth <= 1; return fib( $nth - 1 ) + fib( $nth - 2 ) }

I've found that many companies are already using experimental signatures in their production code because of how many bugs they prevent. Adding in a handful of optional types (and allowing class names), would tremendously improve the readability of Perl and help to bring it up to modern standards. We're almost there for signatures, but they've been experimental for four years and with the recent fix to deal with the subroutine attribute problem, they'll be experimental for at least another two years.

As for OO programming, Larry deliberately added bare-bones functionality to see what people would build on top of it. If you read perlootut it explains the basics of OO, but that's for new developers. Experienced developers coming to Perl will already know that. And they'll see the following CPAN modules referenced:

  • Moose
  • Moo
  • Class::Accessor
  • Class::Tiny
  • Class::Tiny::Antlers
  • Role::Tiny

Of course, at that point we say "but this means you can choose the OO system that really meets your needs. I count around 70 OO systems (with more on the way) on the CPAN. That's not counting the many "in-house" systems I've found at clients. Our popular competitors, such as PHP, Ruby, and Python, also have "minimal" OO systems, but those are much easier to use than Perl's minimal OO system. Here's a simple Point class:

Ruby:

class Point attr_accessor :x attr_accessor :y def initialize x, y @x = x @y = y end def inverted return Point.new @y, @x end end

PHP:

class Point { public $x, $y; public function __construct ($x, $y) { $this->x = $x; $this->y = $y; } public function inverted() { return new Point( $this->y, $this->x ); } }

Python:

class Point: def __init__(self, x, y): self.x = x self.y = y def inverted(self): return Point(self.y,self.x)

Core Perl 5:

package Point; use Carp 'croak'; sub new { croak('…') unless @_ == 3; my ( $class, $x, $y ) = @_; return bless { x => $x, y => $y } => $class; } sub x { croak('…') unless @_ == 1 || @_ == 2; my $self = shift if ( @_ ) { $self->{x} = shift; } return $self->{x}; } sub y { croak('…') unless @_ == 1 || @_ == 2; my $self = shift if ( @_ ) { $self->{y} = shift; } return $self->{y}; } sub inverted { croak('…') unless @_ == 1; my $self = shift; return Point->new( $self->y, $self->x ); } 1;

Is it any wonder that Perl has a certain reputation? It gets better with Moo or Moose, but those are CPAN modules and by not having a clean, minimal OO syntax in the Perl 5 core, we have 70+ CPAN OO systems competing for mindshare. Most of our competitors have very little competition in this space, which means that they don't have to spend so much time learning the OO module of the week that some companies adopted. Plus, the OO features can be optimized at the core language level (a huge win).

Here's a simple syntax I toyed with for Perl 5 (hard-coded class name in the inverted method just for an example):

class Point { has Num ($x, $y); method inverted () { return Point->new( x => $y, y => $x ); } }

That still feels like Perl 5 (to me). I tried to make it as minimal a change as possible and it's still a much cleaner syntax. Or here's an LRU cache written in this shorter syntax:

class Cache::LRU { use Hash::Ordered; our $num_caches = 0; # class data (unused +in this example) my $x = Hash::Ordered->new; # private instance da +ta has UInt $max_size = 20; # public instance dat +a method set ( Str $key, $value ) { if ( $x->exists($key) ) { $x->delete($key); } elsif ( $x->keys > $max_size ) { $x->shift; } $x->set( $key, $value ); } method get ( Str $key) { return unless $x->exists($key); return $x->set( $key, $x->delete($key) ); } }

That's very easy to read and generally leverages familiar syntax. It would also be just enough sugar to easily build upon, but also offers a much nicer core OO syntax than many competing languages. The key for something like that is to keep paring away features to ensure we have the minimal syntax necessary to get it done. For example, don't like the method keyword with the implicit invocant? We could forget about it and just use the existing sub functionality and supply the invocant—$self—manually. No new code to write in the Perl core! We can get there and have a shinier language (maybe even something others would take a look at), but we'd need the smallest proposal possible and fight hard to avoid bike-shedding. Oh, and we'd need it implemented, too :) We know it can be done because there are similar modules on the CPAN. Why not do it?

For comparison, here's that code in Moose:

package Cache::LRU { use Hash::Ordered; use Moose; use MooseX::Types::Common::Numeric qw/PositiveOrZeroInt/; use Carp ‘croak’; use namespace::autoclean; has '_cache' => ( is => 'ro', isa => 'Hash::Ordered', default => sub { Hash::Ordered->new }, ); has 'max_size' => ( is => 'ro', isa => PositiveOrZeroInt, default => 20, ); sub set { croak('…') unless @_ == 3; my ( $self, $key, $value ) = @_; if ( $self->_cache->exists($value) ) { $self->_cache->delete($value); } elsif ( $self->_cache->keys > $self->max_size ) { $self->_cache->shift; } $self->_cache->set( $key, $value ); } sub get { croak('…') unless @_ == 2; my ( $self, $key ) = @_; if ( $self->{cache}->exists($key) ) { my $value = $self->_cache->delete($key); $self->_cache->set($key,$value); return $value; } return; } __PACKAGE__->meta->make_immutable; }

Replies are listed 'Best First'.
Re: Recap: The Future of Perl 5
by tobyink (Canon) on Aug 23, 2018 at 18:18 UTC

    Firstly…

    Class::Tiny::Antlers

    Whaaaaat?!?! One of my modules is referenced by the core Perl documentation? How did that happen?!

    Secondly, the Moose example could be simplified quite a bit. I feel you're making it deliberately verbose by not using sub signatures for example.

    package Cache::LRU { use Hash::Ordered; use Moose; use MooseX::Types::Common::Numeric qw/PositiveOrZeroInt/; use namespace::autoclean; has '_cache' => ( is => 'ro', isa => 'Hash::Ordered', default => sub { Hash::Ordered->new }, ); has 'max_size' => ( is => 'ro', isa => PositiveOrZeroInt, default => 20, ); sub set ($self, $key, $value) { if ( $self->_cache->exists($value) ) { $self->_cache->delete($value); } elsif ( $self->_cache->keys > $self->max_size ) { $self->_cache->shift; } $self->_cache->set( $key, $value ); } sub get ($self, $key) { return unless $self->_cache->exists($key); return $self->_cache->set( $key, $self->_cache->delete($key) ) +; } __PACKAGE__->meta->make_immutable; }

    Still more verbose than your proposed syntax, but not as bad as it was. Also, if you used Moo, you could avoid the make_immutable stuff because the constructor automatically immutablizes the class the first time it gets called.

    To be honest, Moose seems verbose because this is such a simple class. It means that boilerplate imports and stuff starts taking up a significant portion of the class. In a longer class with more methods, the difference would be a lot less pronounced.

    I'mma give a Mew example because Mew is kinda cool.

    package Cache::LRU { use Hash::Ordered; use Mew; has _cache => InstanceOf['Hash::Ordered'], (default => sub { Has +h::Ordered->new }); has max_size => PositiveOrZeroInt, (default => 20); sub set ($self, $key, $value) { if ( $self->_cache->exists($value) ) { $self->_cache->delete($value); } elsif ( $self->_cache->keys > $self->max_size ) { $self->_cache->shift; } $self->_cache->set( $key, $value ); } sub get ($self, $key) { return unless $self->_cache->exists($key); return $self->_cache->set( $key, $self->_cache->delete($key) ) +; } }

      ++ from me and I agree with all of your points (except I didn't make it deliberately verbose because it was thrown together quickly; my bad).

      However, Mew, like Moose, or Moo, is not core. Not having a clean, minimal syntax for core OO is part of the problem I would love to see us address (and obviously, I mean more than bless and @ISA).

Re: Recap: The Future of Perl 5
by RonW (Parson) on Aug 23, 2018 at 23:05 UTC

    Note that Perl does ship with a (still) core module called Class::Struct, which offers a simple syntax for OO programming.

    An example I wrote 2 years ago is somewhat similar to the Point class example you provided. I only wrote it as a rebuttal to a claim that Perl OO programming is very hard without installing something from CPAN

    package Planet; use Class::Struct mass => '$', radius => '$'; use Math::Trig; sub density { my $self = shift; my $vol = 4/3*pi*($self->radius)**3); return $vol/($self->mass); } package main; use Planet; my $Jupiter = Planet->new(mass => 1.8E27, radius => 6.9E7); print "Jupiter has mass of " . $Jupiter->mass . " and "; print "radius of " . $Jupiter->radius . "\n"; print "It's density is " . $Jupiter->density . "\n";

    Not as clean as your proposed syntax, but is something that can be used without needing anything from CPAN. And there are limitations, such as Class::Struct classes can only inherit from UNIVERSAL.


    Edit: corrected syntax as pointed out by LanX. Also, changed mod:// links to doc://

        Agreed, but it's not core Perl

        I think it would aid clarity to your arguments immensely if you could choose a different word for what you want. The Perl community has been using "core" to mean precisely modules which are bundled with the perl distro since last millennium (and I know you know this too). It's confusing if you start to use it to mean something else.

        I agree, your proposed syntax is much nicer.

        I did see somethings surprising.

        In:

        class Point { has Num ($x, $y); method inverted () { return Point->new( x => $y, y => $x ); } }

        you have $x instead of $self->x (likewise for $y).

        I found this surprising, and had to pause to think for a second or 2. How common is it in other OO languages to not need "self" to reference instance variables in instance methods? Personally, I don't recall ever seeing it done without "self". (Been so long since I last used C++ that I had forgotten.)

        More surprising was:

        class Cache::LRU { use Hash::Ordered; our $num_caches = 0; # class data (unused +in this example) my $x = Hash::Ordered->new; # private instance da +ta has UInt $max_size = 20; # public instance dat +a method set ( Str $key, $value ) { if ( $x->exists($key) ) { $x->delete($key);

        where $x is also an instance variable.

        (Just some additional thoughts.)

      Hi RonW,

      I did not know about this Class::Struct module, this seems to be quite interesting. I'll have a look at it. Thank you for having pointed to it.

      >   use Class::Struct mass => $, radius => $;

      How is this legal Perl syntax? A short glimpse into Class::Struct seems to indicate that you rather meant

        use Class::Struct mass => '$', radius => '$';

      Right?

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

        You're right. It was in a reply I wrote quickly and didn't get to test before I posted.

        (Also, thanks for the doc:// linking tip.)

Re: Recap: The Future of Perl 5
by tobyink (Canon) on Aug 24, 2018 at 10:24 UTC

    I am an XP whore and if I reply multiple times, I'll get more XP. So have more thoughts.

    Point Class

    Your Perl version of the Point class can be simplified a lot using the features of existing modern versions of Perl.

    use v5.20; use experimental 'signatures'; package Point { sub new ($class, $x, $y) { bless [$x, $y] => $class; } sub x ($self) { $self->[0] } sub y ($self) { $self->[1] } sub inverted ($self) { ref($self)->new($self->y, $self->x); } }

    Yeah, I cheated by making x and y read only, but dammit they should be read only. If they change, it's a different point, so you should make a different object.

    Incidentally, making them read only makes the implementation simpler in Perl, but harder in the other languages you gave examples for.

    Type Checking in Core

    As for type checking in core, there's already parser support for:

    my Foo $foo = Foo->new(...);

    However, other than checking that a package "Foo" exists, core Perl doesn't do anything with that.

    I think the best route for adding decent type support to Perl would be:

    1. Provide an API which allows a type library module to define types such as my Str $x where Str isn't a package name and $x would be a plain old string not a blessed reference. These should be scoped (either lexically or by package).

      If you try to use a type which hasn't been declared for lexical scope, Perl assumes you meant it as a class name.

      I'll provide an outline of a simple and perhaps workable API below.

    2. Bundle a module with core which defines a bunch of predefined types. Perhaps automatically load it if a recent enough use VERSION line is given.

      Including the following in the core set of types shouldn't be controversial because they conform roughly to the existing types Perl kinda exposes to end users already.

      • All the standard ref types: "ArrayRef", "HashRef", "ScalarRef", "CodeRef", "GlobRef", "RegexpRef", etc.
      • The types of value that can be stored in an SV: "Int", "UInt", "Num", "Str".
      • "Defined" and "Blessed".

      Having a type called "Undef" would probably be non-controversial, but it's also pretty useless to declare a variable which will be undef and which will always be undef.

      There are loads of other types which are often handy, like "PositiveNum" but which seem likely to end up in bikeshedding. (For example, should "PositiveNum" include zero because that's more useful in most cases, even if zero isn't strictly positive. Should it be called "UNum" for consistency with "UInt", even though the former isn't a well-established name like the latter is, and "Unum" is Latin for "one" in the accusative form. Should it accept overloaded objects? Et cetera.)

      To avoid such bikeshedding, plus to allow people to define their own useful application-specific type constraints, I think the core set of types should be kept small and non-controversial, with the API mentioned above being used to define other types.

    3. Allow these types to also be used in sub signatures.

      sub foo (Int $bar, Str $baz) { ...; }

      Would be roughly equivalent to:

      sub foo { my Int $bar = $_[0]; my Str $baz = $_[1]; die "..." if @_ > 2; ...; }

    Sketch of an API for Pluggable Types

    If Perl encounters:

    my Foo $x = $y;

    It should interpret "Foo" much the same way it would in the case of Foo->new(). That is, if there's a Foo sub, Perl should treat it as Foo(). If there's no "Foo" sub, then "Foo" is treated as a package name (string). Perl would make this decision based on whether the Foo sub existed at compile time.

    After that, any time a value is assigned to $x, then one of the following happens:

    "Foo"->CHECK_SCALAR(\$newvalue); Foo()->CHECK_SCALAR(\$newvalue);

    I'm naming the method CHECK_SCALAR instead of just CHECK to allow for it to possibly be extended to arrays and hashes. Passing the value by reference for added efficiency, and because checking arrays and hashes will likely require passing a reference anyway.

    This would be the basic API for creating user-defined types. There should probably also be an XS API which would allow a value to be checked via a single xsub call rather than all that method dispatch stuff. The basic type library bundled with core would make use of the XS API, because I'm pretty sure we don't want using the most basic type checks in signatures to slow down our code very much.

    Oh yeah, and also:

    sub UNIVERSAL::CHECK_SCALAR (Str $class, ScalarRef $sref) { require Scalar::Util; unless (Scalar::Util::blessed($$sref) && ${$sref}->isa($class)) { require Carp; Carp::croak("..."); } }
      However, other than checking that a package "Foo" exists, core Perl doesn't do anything with that.
      It's used for compile-time and run-time checking of the keys to hash refs. It's the mechanism that fields.pm uses.

      Dave.

        Hmm, interesting. Do you know how much of that is in the core Perl (as Ovid is using the term "core"… perl.exe itself, no modules), and how much is done by fields.pm and friends?

        If it's mostly done by the modules, then I don't see a problem with there being multiple ways of hooking into my Foo $x. TIMTOWTDI after all.

      Provide an API which allows a type library module to define types such as my Str $x where Str isn't a package name ...

      Again, that fails horribly, IMHO. For the shops which adopt that, every time I go into a shop, I have to discover how their implementation of "Str" is different from the implementation of "Str" at my last shop, which in turn is different from the implementation of "Str" I would expect in other languages. Core types that people expect should be in the core (and not in libraries I have to remember to load!). Further, their behavior should be as boring and predictable as possible. As for an extensible API? Sure, I can get behind that if the arguments which would invariably ensue are brief. Otherwise, the bike-shedding would stand a good chance of guaranteeing that nothing would get done.

      Bundle a module with core which defines a bunch of predefined types. Perhaps automatically load it if a recent enough use VERSION line is given.

      Again, if you have to remember to do a particular thing to get benefits, it hurts the Perl community. For example, use strict; use warnings; should have been the default a long time ago, with additional action taken to disable them. It's less common now, but we still have to fight to remind developers to enable strict. That's years of arguing over a basic issue that could have been spent more profitably writing code and developing the language.

        Even with something as simple as Str, if you put ten Perl developers in a room, I'd expect them to have eleven different opinions on how it should be implemented.

        my Str $x = $y;
        • If $y is undef, then $x should be "" because of type casting.
        • If $y is undef, then there should be an exception thrown because undef isn't a string.
        • If $y is an overloaded object, then $x should now be an overloaded object.
        • If $y is an overloaded object, then $x should now be the stringified version of that object.
        • If $y is an overloaded object, then there should be an exception thrown because an object isn't really a string and overloading sucks anyway.
        • Even if $y is a non-overloaded object, $x should now be the stringified version of that object.
        • If $y is 42 (no quote marks), then there should be an exception thrown because integers aren't a subset of strings dammit!
        • If $y is 42 (no quote marks), then $x should be "42" (with quotes) and poking around with B:: stuff you should be able to tell the difference between them.
        • A lot of these cases could be handled by using warnings instead of exceptions. It's basically the same kind of error as use warnings "numeric" anyway, and we're happy enough having those be non-fatal.
        • Empty strings are always an error in my application, so if $y is "", it should throw an exception, shut down the computer, and set the building on fire. (In all seriousness, I know of at least one case in Perl where empty strings are special-cased, treating them differently from non-empty strings.)
        • But dualvars! Waaaaaah!

        Ovid:

        Again, if you have to remember to do a particular thing to get benefits, it hurts the Perl community. For example, use strict; use warnings; should have been the default a long time ago, with additional action taken to disable them.
        So, Joe SysAdmin does an upgrade one day, and finds half of the old perl scripts on the system are broken. This is not something you want to have happen, not when we're still fighting the fallout from the "perl is dead" smear campaign.

        I could see an argument that stuff like "use 5.10.0" should've implied strict and warnings also... or for that matter that cromatic's Modern::Perl should ship with the core library, it's a lot easier to type correctly (I had to double-check that you really need "5.10.0").

        Backwards compatibility is really and truly important, and it remains important-- it's one of the things that perl has always gotten right (and the new kids keep screwing up).

Re: Recap: The Future of Perl 5
by shmem (Chancellor) on Aug 23, 2018 at 22:29 UTC

    I'm looking only at your first examples in that post for now.

    To write a robust Fibonacci function, you might write something like this:
    sub fib { croak("…") if @_ != 1; my $nth = $_[0]; croak("…") if not defined $nth; croak("…") if $nth !~ /^+?\d+$/a; return $nth if $nth <= 1; return fib( $nth - 1 ) + fib( $nth - 2 ); }

    You are being unnecessarily verbose, aren't you? All those checks just fold into two:

    my $fib_die_msg = 'fib() takes one positive integer as argument, abort +ed'; sub fib { my($nth = shift) =~ m{^\d+$} or die $fib_die_msg; @_ and die $fib_die_msg; return $nth if $nth <= 1; return fib( $nth - 1 ) + fib( $nth - 2 ); }

    The two checks can also be written on one line and no harm done:

    sub fib { my($nth = shift) =~ m{^\d+$} and ! @_ or die $fib_die_msg; ... }

    The checks can be skipped completely, if the caller is the recursive subroutine itself:

    sub fib { my $nth = shift; (caller)[3] eq 'fib' or $nth !~ m{^\d+$} or @_ and die $fib_die_msg; return $nth if $nth <= 1; return fib( $nth - 1 ) + fib( $nth - 2 ); }

    Optional typing in signatures would improve that, for sure - but there's Params::Validate for instance. Then, what should happen if a sub takes an Uint and gets a polymorphic object which resolves to a Uint in numeric context via overloading, but as a filehandle at I/O ? Would we need typecasting? Would the object be aliased to its numeric interpretation in such a subroutine body, or would other slots and/or behaviors still be available and valid?

    I personally believe that such constraints go against the Camel's hair, and against the best thing perl has since sliced bread, which is context awareness, which forces the programmer to be aware of the context in which they are moving things around.

    Also, neither your Ruby, PHP nor Python versions do any parameter checks, but you write them for your perl5 version. Write an equivalent Python class, and you will have exexption definitions and try/catch branches and such.

    No need to be unfair to perl to make a point - and no need to blow away the very foundations of perl either ;-)

    perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'

      Regarding this code:

      sub fib { my $nth = shift; (caller)[3] eq 'fib' or $nth !~ m{^\d+$} or @_ and die $fib_die_msg; return $nth if $nth <= 1; return fib( $nth - 1 ) + fib( $nth - 2 ); }

      That's a perfect example of the sort of things that an expert Perl programmer can understand, but it's not welcoming to newcomers. It also has two serious bugs I see off the top of my head (and that's before parsing precedence, which I can't with that example).

      Optional typing in signatures would improve that, for sure - but there's Params::Validate for instance. Then, what should happen if a sub takes an Uint and gets a polymorphic object which resolves to a Uint in numeric context via overloading, but as a filehandle at I/O ? Would we need typecasting? Would the object be aliased to its numeric interpretation in such a subroutine body, or would other slots and/or behaviors still be available and valid?

      Again, while Params::Validate is an excellent module, it is not in core and, if it was, it still means it's not core Perl and it's the sort of thing we'd have to remember to use (and to be honest, the confusing interface is an artifact of the fact that it's not core perl).

      I'm specifically arguing that core features developers expect in many mature languages should be core to the Perl language itself, and not provided by external modules.

      And your comments about "resolves to a UInt" are the sort of issues that many other languages need to resolve. That problem isn't unique to Perl, so Perl should find a way to solve it in a way that fits "the principle of least surprise." I completely agree that we have to make some hard choices (should the string "3" map to the integer 3?), but we shouldn't let these sorts of questions stop us from solving this problem.

        That's a perfect example of the sort of things that an expert Perl programmer can understand, but it's not welcoming to newcomers. It also has two serious bugs I see off the top of my head (and that's before parsing precedence, which I can't with that example).

        I agree, my bad.

        Care to elaborate about the bugs? I see that fib "5\n" would be allowed, so the matching should use \z; then, restrict to ascii via the /a modifier. Precedence is ambiguous (and it shows not to work as intended), so parens are necessary and that should be:

        (caller)[3] eq 'fib' or ($nth !~ m{^\d+\z}a or @_) and die $fib_die_ +msg;

        Something else?

        Of course, this is an acme example only given for showing "it can be done", but such constructs have poor maintainability - which is the entry point of your talk, I guess.

        I totally agree with you that good function signatures and an optional, gradual typing system would be great to have, for some style of programming. But looking the other way round, that would weaken a great strength of perl:

        Perl is amazingly good at being lousy at typing.</sarcasm>

        For the sake of orthogonality, variable type constraints of course would need to be available for every kind of declaration. Currently, the function signature stuff misses that, but it can be done - not with core perl as you say, but with packages belonging to the core, e.g.

        use strict; use warnings; use 5.20.0; use Attribute::Handlers; package Uint { require Tie::Scalar; @Uint::ISA = qw(Tie::StdScalar); sub STORE { my ($p,$f,$l) = caller; $_[1] !~ /^\d+\z/a and die "not a positive integer in package $p file $f line + $l, aborted"; ${$_[0]} = $_[1]; }; } sub UNIVERSAL::Uint : ATTR(SCALAR) { tie ${$_[2]}, 'Uint' } sub fib { my $nth : Uint = shift; # check for extra items in @_ missing return $nth if $nth <= 1; return fib( $nth - 1 ) + fib( $nth - 2 ); } say fib @ARGV;

        at the cost of lousy performance. On my machine, a fib(30) is

        • 0m0.923s plain (with no checks)
        • 0m3.688s with my version (with fixes from above)
        • 4m4.759s with Attribute::Handlers and Tie::Scalar

        I'm far away and below of the current affairs and state of perl development/improvement, but implementing attribute handling in the core, type constraints via short-circuiting tie for common types (facilitating user-defined types) and allowing attributes for function signature variable attributes in the parser e.g.

        sub fib :prototype($) ($nth : Uint) { # check for 1 == @_ already done ... }

        probably implementing all that in XS first, would be a huge gain.

        There are some things I would wish to have in core proper apart from that and inlining via keywords (and modifiers where sensible):

        • iterators as first class citizens
        • inside-out hashes and arrays (think Hash::Util::FieldHash as default implementation, connecting slots would require a keyword)
        • more contexts than void, scalar, list
        • return constraints inside a subroutine (think Uint sub fib ($nth : Uint) where returning anything but an Uint is an error)
        • user defined parser extensions (rewrite from bison/yacc to perl)
        • ...

        but then, I should start to explore what is called perl 6, I guess, and many of these things surely have been discussed long ago.

        perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
Re: Recap: The Future of Perl 5
by Laurent_R (Canon) on Aug 23, 2018 at 22:06 UTC
    ++ovid, you obviously make your points much better than I possibly could from sheer memory of what you said.

    I'm still quite happy that I launched the discussion about the ideas you developed in that keynote in Glasgow.

Re: Recap: The Future of Perl 5 (class)
by Arunbear (Prior) on Sep 03, 2018 at 10:58 UTC

    Re the OO part - there are a few problems with the class idea. Firstly, how do class and package relate? Are they synonymous? Can you have one inside the other?

    On a somewhat higher level, what if the class construct isn't the best way to do OOP.

    Some newer languages (Go and Rust) have avoided having a class construct (but still support some flavour of OOP).

    And going back much further, we find works like the GOF book, which was based around two principles

    • Program to an 'interface', not an 'implementation'
    • Favor 'object composition' over 'class inheritance'
    So instead of just copying a sub-optimal construct that some other languages copied from others, maybe we ought to step back and look for better abstractions?

    My own lengthy contemplation has led to a prototype of what such abstractions could look like. Mic provides Interfaces (which support Eiffel-like contracts) as a primary abstraction, and Implementations as a secondary abstraction.

    I worry that what gets implemented would be "weaker" than what can already be achieved using libraries.
Re: Recap: The Future of Perl 5
by EvanCarroll (Chaplain) on Aug 24, 2018 at 10:57 UTC
    For fairness you should show Moo for the point example,
    package Point { use Moose; has [qw/x y/] => ( isa => 'Int', is => 'rw' ); sub inverted { my $self = shift; __PACKAGE__->new( x => $self->y, y => $self->x ); } }
    Shy of that, this argument seems to mostly boil down into not core. This seems to be very much a reiteration of Stevan's thinking and work with Moxie. Good luck on it, it's just not very interesting to me. Out of all the problems I've had with Perl the inclusion of Moose in CORE, or a lightweight object system doesn't make the top-10. In my estimation the best thing Perl has going for it is Moose.


    Evan Carroll
    The most respected person in the whole perl community.
    www.evancarroll.com
Re: Recap: The Future of Perl 5 (backwards comaptibility, RFC)
by LanX (Saint) on Aug 31, 2018 at 18:23 UTC
    We can easily speculate about a new "sexy" syntax which "Easy things should be easy, and hard things should be possible"°.

    The crucial problem for the acceptance is backwards compatibility , both semantically and syntactically.

    Most of us are stuck with an older version of Perl before the team decides to upgrade, so it would be very important for acceptance if the "sexy" syntax could already be run there.

    NB: It doesn't need to be fast ... just complete. referring to a new version for speed could be a good incentive and marketing.

    let me suggest something, would this syntax be acceptable?

    { use class Point => ISA-LIST; my ($x, $y); my $z :ro; our @points; # class var sub inverted :public { return Point->new( x => $$y, y => $$x ); } } #end of scope

    please note that use class Point could be shortend to class Point with Keyword::Simple for Perl > 5.13

    The whole mechanism would work with a safe source filter² which just injects some boilerplate OO package header at the _beginning_, and a callback which is triggered at the end of scope.*

    The callback would inspect the STASH of the package and use PadWalker to identify instance variables.

    The indentified subs (like "inverted") would be patched and wrapped with initialising code mapping the first argument to $self ( a closure var from the boilerplate).

    Wrapper would look like something like

    my $old_meth = \&Point::inverted; *Point::inverted = sub { $self = shift @_; $x = \ $self->{x}; $y = \ $self->{y}; goto &$old_meth; }

    ( I deliberate used references for instance vars, to have a visual clue)

    This and the use of attributes for more "exotic" features would allow us to stay comaptible to

    • the old perl model, since $self still works
    • and shorten the syntax like our competitors.
    • we could stay compatible to older OO code from the most popular OO frameworks (M** whatever) by using this syntax as proxy frontend
    • people comming from other languages like Python would feel instantly at home
    • we can not only auto-generate ->new but also go by the Python convention to have a constructor sub called Point()
    (DISCLAIMER: I didn't check yet the timing of attributes and triggering of the scope callback yet)

    Of course the "real" implementation would use dedicated opcodes and ammend the parser, hence be mmore performand.

    Comments?

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

    °) didn't that use to be Perl's motto? (update: yep)

    *) NB you don't need the block if there is only one class per file.

    ²) safe because the source filter is just injecting code at the beginning and doesn't attempt to parse Perl.

      > but also go by the Python convention to have a constructor sub called Point()

      to elaborate further

      this:

      class Point: def __init__(self, x, y): self.x = x self.y = y def inverted(self): return Point(self.y,self.x)

      could be translated to

      class Point; sub INIT($X,$Y) { $$x = $X; $$y = $Y; # or $self->{y} = $Y; } sub inverted { return Point($$y,$$x) # or Point->new($$y,$$x) # or Point->new($self->{y},$self->{x}) # or Point($self->{y},$self->{x}) # ... TIMTOWTDI }

      People comming from Python would instantly feel at home and little by little discover more elaborated possibilities by using :attributes.

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

Re: Recap: The Future of Perl 5
by Anonymous Monk on Aug 24, 2018 at 10:02 UTC
    ..twitter... How could you leave Badger off the list?
Re: Recap: The Future of Perl 5
by Thenothing (Sexton) on Nov 01, 2018 at 23:09 UTC
    Related:

    https://www.youtube.com/watch?v=lnleCcKqcv4

    https://www.youtube.com/watch?v=ugEry1UWg84

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://1220917]
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others examining the Monastery: (7)
As of 2024-03-28 12:51 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found