TL;DR: I believe it impossible to write a dual-life module using function prototypes that works on both perl 5 and perl 7. I have been experimenting with what it might be like to maintain a dual perl5-and-7 module that uses prototypes on functions. Core's List::Util would be a good candidate except it's all written in XS, so never mind. Next on my list is List::UtilsBy. Right now it has lots of code looking like sub sort_by(&@) { ... } So, first off I trivially just rewrite all of those into sub sort_by :prototype(&@) { ... } And hey presto, my module works fine on 5.20 onwards, and presumably 7. Now all I have to do is handle the pre-5.20s, right? ;) HAH! $ perl5.18.2 lib/List/UtilsBy.pm Invalid CODE attribute: prototype(&@) at lib/List/UtilsBy.pm line 147. So my first thought was to inject a MODIFY_CODE_ATTRIBUTES. Simply put, this doesn't work. A naive attempt which invokes Sub::Util::set_prototype silently has no side-effect - it doesn't warn or fail, it just doesn't set the prototype. I haven't investigated why yet, but I have a bug open: https://rt.cpan.org/Ticket/Display.html?id=132889 I think it's probably because MODIFY_CODE_ATTRIBUTES runs too early in the parser. I have to defer it until UNITCHECK time. See more code above. While that works, it produces lots of warnings: CODE package attribute may clash with future reserved word: prototype at lib/List/UtilsBy.pm line 147. OK, well I can sortof fix that one too, by injecting a `no warnings 'reserved'". So the current code in List/UtilsBy.pm looks like: BEGIN { if( $] < 5.020 ) { require Sub::Util; Sub::Util->VERSION( '1.40' ); warnings->unimport( qw( reserved ) ); my @prototypes; *MODIFY_CODE_ATTRIBUTES = sub { my ( $pkg, $code, @attrs ) = @_; my @ret; foreach my $attr ( @attrs ) { if( $attr =~ m/^prototype\((.*)\)$/ ) { my $prototype = "$1"; push @prototypes, [ $code, $prototype ]; next; } push @ret, $attr; } return @ret; }; UNITCHECK { foreach ( @prototypes ) { my ( $code, $prototype ) = @$_; Sub::Util::set_prototype( $_->[1], $_->[0] ); } } } } And note that there are two large problems still unaddressed here: 1) This code has to suppress all "reserved" warnings, not just those relating to the :prototype attribute 2) This code has to appear in List/UtilsBy.pm directly because of that UNITCHECK phaser. It would not be possible to inject this entire thing via a module. Point 1 is a slight annoyance, but point 2 really is the killer. If we're already worried about people having to copy lines of boilerplate about "use strict; use warnings;" around the place I'd hate to have to copy the above ~30 lines of code between every one of my prototypes-using modules. I had originally hoped to move all this code into a simple module we could `use`: use Sub::Attribute::Prototype; Such a module would be a no-op on 5.20+, and inject something like the above code on earlier perls. But the presence of that UNITCHECK phaser appears to make this impossible. I haven't investigated the RT bug linked above yet, but I suspect it won't be an easy fix because of the order in which operations happen. Operations that are already fixed in the design of older perl interpreters; remember this is a pre-5.20 only problem. Even if I do find a fix for that issue, it will likely need a new version of the XS module, which adds a little annoyance but not impossible. That still leaves unfixed the `reserved` warnings though. Someone - please prove me wrong. Please demonstrate a way to write a prototype-using module that will work nicely on perls older than 5.20, and also on 7. I allege it cannot be done. -- Paul "LeoNerd" Evans leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/Thread Next