Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
PerlMonks  

Re: Recap: The Future of Perl 5

by tobyink (Canon)
on Aug 24, 2018 at 10:24 UTC ( [id://1221004]=note: print w/replies, xml ) Need Help??


in reply to Recap: The Future of Perl 5

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("..."); } }

Replies are listed 'Best First'.
Re^2: Recap: The Future of Perl 5
by dave_the_m (Monsignor) on Aug 24, 2018 at 11:02 UTC
    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.

        It's built into the perl executable itself:
        BEGIN { %Foo::FIELDS = qw(a 1 b 1) }; my Foo $x = {}; $x->{c} = 1; # compile-time error
        fields.pm just populates the field hash

        Dave.

Re^2: Recap: The Future of Perl 5
by Ovid (Cardinal) on Aug 24, 2018 at 11:35 UTC
    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!

        The point of optional, gradual typing is to offer optional type safety. If I say "String", it must be defined and have an appropriate string value. Thus, undef would cause an exception. I would suggest that any non-overloaded reference would also throw an exception (No more printing out ARRAY(0x7fecea803280)). However, if stringification is directly overloaded, we'd have to respect the intent because printing DateTime->now is intended to work. The integer 42 would need to stringify to 42 because even Java allows this as the one case of operator overloading because it's so damned useful.

        That being said, I could be smoking crack. And I'd want these to be exceptions, not warnings, because I find so many ways in which warnings can be overlooked.

        I'd be quite fine with eleven different opinions on how Str should be implemented. The Perl developers should then talk to each other, look for a consensus and make this the way it is done.

        It is the subtle differences between Perl's (CPAN) object systems which drives me nuts, not the individual behaviour of any of these systems. I can live just fine with any of them, but having more than one in a project, which is inevitable for bigger projects, hurts.

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

        use v5.16 and above does enable strict (though not warnings).

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://1221004]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (7)
As of 2024-04-19 11:52 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found