Re: Pure perl lexical sub import
by Corion (Patriarch) on Dec 21, 2024 at 06:13 UTC
|
If you don't want to take code from Exporter, you can do it like this:
BEGIN {
*looks_like_number = \&Scalar::Util::looks_like_number;
}
#!perl
use 5.020;
use Scalar::Util ();
BEGIN {
*looks_like_number = \&Scalar::Util::looks_like_number;
}
say looks_like_number(42);
| [reply] [d/l] [select] |
|
|
I think this will still suffer from littering his method namespace.
| [reply] |
Re: Pure perl lexical sub import
by LanX (Saint) on Dec 21, 2024 at 12:40 UTC
|
I had/have a project allowing to "export" lexicals.
Basically a source filter which isn't filtering but only injecting code when import() is called.
(Yes it's safe, since there is no filtering/parsing involved)
It never made it to CPAN but can be found on GitHub.
https://github.com/LanX/Filter-Inject
| [reply] |
|
|
Can you give a short synopsis of what is happening there? Where does the injection take place?
| [reply] |
|
|
Does this meet your requirements?
Test: cat t/01-use.t
package __test__;
use v5.22;
use warnings;
use Test::More;
use lib "../lib";
sub dummy {}
{
my $line = __LINE__;
use lexical 'Scalar::Util' => qw/looks_like_number/;
is(__LINE__, $line+2, "line numbers are not messed up ");
is( defined(&looks_like_number) ,1 ,"sub exists");
is( looks_like_number("42"), 1, "42 is number" );
isnt( looks_like_number("XX"), 1, "XX isn't number" );
is(defined &__test__::dummy, 1, "namespace testable");
isnt( defined(&__test__::looks_like_number) ,1 ,"namespace is clea
+n");
}
isnt( defined(&looks_like_number) ,1 ,"lex sub doesn't exist out of sc
+ope");
diag( "Testing lexical $lexical::VERSION, Perl $], $^X" );
done_testing;
test run
perl /home/lanx/perl/prj/lexical/t/01-use.t
ok 1 - line numbers are not messed up
ok 2 - sub exists
ok 3 - 42 is number
ok 4 - XX isn't number
ok 5 - namespace testable
ok 6 - namespace is clean
ok 7 - lex sub doesn't exist out of scope
# Testing lexical 0.01, Perl 5.038002, /usr/bin/perl
1..7
Module: cat lib/lexical.pm
package lexical;
use v5.22;
use strict;
use warnings;
use Filter::Util::Call ;
=head1 NAME
lexical - Lexical use of exported functions
=head1 VERSION
Version 0.01
=cut
our $VERSION = '0.01';
=head1 SYNOPSIS
Pragma to import subs as lexical_subs into the current scope
use lexical "Scalar::Util" => qw/looks_like_number/;
print looks_like_number("a15");
=head1 EXPORT
No classic exports, the namespace isn't polluted
=head1 SUBROUTINES/METHODS
=head2 import
=cut
sub import {
my ($my_pkg, $module, @imports) = @_;
eval "use $module"; # TODO quick & dirty
my $code = q(use experimental 'lexical_subs','refaliasing';);
$code .= << "__CODE__" for @imports; # TODO only subs ATM
my sub $_;
\\&$_ = \\&${module}::$_;
__CODE__
upject($code);
}
sub upject {
my $injection = shift;
# --- exit if undef
return unless defined $injection;
# --- adjust line number to disguise injection
my ($file,$line) = (caller(2))[1,2];
$line++;
$injection .= qq{\n# line $line "$file"\n};
#warn $injection;
# --- add source filter
filter_add
(
sub {
my $status =
filter_read_exact(1); # read one char into $_
if ( $status > 0) {
$_ = $injection .";".$_; # prepend code once
filter_del(); # delete source filter
}
$status ;
}
);
}
=head1 AUTHOR
Rolf Michael Langsdorf, C<< <lanx at cpan.org> >>
# yadda yadda ...
1; # End of lexical
| [reply] [d/l] [select] |
|
|
|
|
|
|
|
> Can you give a short synopsis of what is happening there?
This project does much more than you want, it's realizing macros (well almost) with a use macro() syntax at compile time.
See my presentation from 2019 in Riga: https://perlcon.eu/talk/97
> Where does the injection take place?
Basically is the import() using a source-filter to inject code into the line after the use .
When using that pseudo-module an INC-hook looks for the macro-function.
If you come up with a reasonable name (I already kind of burned Filter::Inject ;) I could publish a module to CPAN doing lexical-imports by wrapping the original module.
use Scoped "MODULE" => ARGUMENTS; ¹
possible names:
- Scoped
- Lexically
- My::Use
- My::Import
- Mine
I'm also not sure if this should rather be lower-cased to indicate a pragma²
¹) in your case:
use lexical Scalar::Util => qw/looks_like_number/;
²) I think lower-case is reasonable here, since if is a pragma too.
| [reply] [d/l] [select] |
Re: Pure perl lexical sub import
by LanX (Saint) on Dec 23, 2024 at 01:20 UTC
|
> but this gives me "Illegal declaration of subroutine looks_like_number".
For completeness, I ran into the same problem, but feature refaliasing is your friend.
According to the docs it was introduced with v5.22
use experimental 'lexical_subs','refaliasing';
my sub looks_like_number;
\&looks_like_number = \&Scalar::Util::looks_like_number;
see also my proof of concept in Re^3: Pure perl lexical sub import (POC)
| [reply] [d/l] [select] |
Re: Pure perl lexical sub import
by Anonymous Monk on Dec 22, 2024 at 16:19 UTC
|
my $looks_like_number = \&Scalar::Util::looks_like_number;
...
... $looks_like_number->( $x ) ...
I personally like this approach because it gives me an easy out if the function is unavailable:
my $looks_like_number = Scalar::Util->can( 'looks_like_number' ) || sub { ... };
| [reply] |
|
|
This is a fairly reasonable solution. Adds a few characters, but still better than the fully qualified name.
| [reply] |
Re: Pure perl lexical sub import
by Arunbear (Prior) on Dec 22, 2024 at 19:12 UTC
|
use v5.36;
package Foo::Internal {
use Scalar::Util 'looks_like_number';
sub check_it ($self, $num) {
say 'Hoorah!' if looks_like_number($num);
}
}
package Foo {
sub new ($class) {
bless {} => $class;
}
no warnings 'once';
*check_it = \&Foo::Internal::check_it;
}
my $foo = Foo->new;
$foo->check_it(42);
$foo->looks_like_number(42);
Then
% perl internal.pl
Hoorah!
Can't locate object method "looks_like_number" via package "Foo" at in
+ternal.pl line 22.
| [reply] [d/l] [select] |
|
|
So you mean basically have a scope where they're imported be a different package than what eventually holds the object methods, by copying out the functions into that other package?
I suppose it works, but I'm having trouble imagining how I would structure things so that it is less ugly than just calling the external function by its full name in the intended package.
| [reply] |
|
|
::::::::::::::
Foo/Internal.pm
::::::::::::::
use v5.36;
package Foo::Internal {
use Exporter 'import';
our @EXPORT = qw[check_it];
use Scalar::Util 'looks_like_number';
sub check_it ($self, $num) {
say 'Hoorah!' if looks_like_number($num);
}
}
1;
::::::::::::::
Foo.pm
::::::::::::::
use v5.36;
package Foo {
use Foo::Internal;
sub new ($class) {
bless {} => $class;
}
}
1;
::::::::::::::
clean.pl
::::::::::::::
use v5.36;
use Foo;
my $foo = Foo->new;
$foo->check_it(42);
$foo->looks_like_number(42);
| [reply] [d/l] |
Re: Pure perl lexical sub import
by LanX (Saint) on Dec 22, 2024 at 23:54 UTC
|
| [reply] |
Re: Pure perl lexical sub import
by LanX (Saint) on Dec 21, 2024 at 13:02 UTC
|
Another approach may be to manipulate the Scratchpad of the scope calling import().
I've never tried to use PadWalker in conjunction with lexical subs, but it might be worth a try.
NB: since PadWalker isn't core this wouldn't qualify as "pure Perl" anymore.
| [reply] |
|
|
| [reply] |
|
|
I did some experiments and I doubt PadWalker can be of help here :/
| [reply] |
Re: Pure perl lexical sub import
by ikegami (Patriarch) on Dec 21, 2024 at 17:23 UTC
|
| [reply] |
|
|
A variation on that is I can say "undef *NAME" for each imported function at the end of the module, but I have to remember to keep that list updated.
| [reply] |