Blog

Exportweltmeister: Perl

24.07.2023 // Renée Bäcker

Viele Module stellen einfach Funktionen zur Verfügung, wie z.B. List::Util die Funktionen first, all, any und noch weitere. Diese kann man in seinen eigenen Code importieren (oder aus Sicht von List::Util dementsprechend exportieren).

Auch in eigenen Modulen kann man so einen Exportmechanismus sehr einfach bereitstellen. Dafür gibt es z.B. die Module Exporter und Exporter::Tiny. Wer es lieber selbst in die Hand nimmt, fügt in seinem Modul eine Funktion import ein.

In der Regel werden Module mit use Modulname; eingebunden. Diese Zeile Code ist im Prinzip das gleiche wie

 BEGIN {
     require Modulname;
     Modulename->import();
}

Im Modul, das die Funktionen zur Verfügung stellt, könnte es so aussehen:

package ExportHello;
  
use v5.38;

use base 'Exporter';

our @EXPORT = qw(hello);
sub hello { say 'Hello World!' };

Damit wird bei einem use ExportHello die Funktion hello importiert. Das hat einen großen Nachteil: Der Namensraum wird zugemüllt mit Funktionen, die evtl. gar nicht von anderem Code aufgerufen werden soll.

package ImportHello;
  
use ExportHello;

hello();

Das führt hello bereits beim Einbinden des ImportHello-Moduls aus. Wenn man das Modul jetzt verwendet:

$ perl -I. -MImportHello -e 1
Hello World!

Die Funktion hello kann aber auch über den Namensraum ImportHello aufgerufen werden:

$ perl -I. -MImportHello -e 'ImportHello->hello()'
Hello World!
Hello World!

Es sieht also so aus, als ob ImportHello die Funktion hello bereitstellt. Was aber gar nicht Sinn des use ExportHello war.

Mit Perl 5.38 ist es jetzt möglich, solche Exporte lexikalisch zu machen, so dass ImportHello zwar die Funktion hello nutzen kann, aber von außen sieht man diese Funktion nicht.

Über das pragma builtin werden einige Funktionalitäten bereitgestellt wie z.B. in Bezug auf Booleans – und jetzt auch die Funktion export_lexically.

package ExportHello;
  
use v5.38;

use experimental 'builtin';
use builtin 'export_lexically';

export_lexically hello => sub { say 'Hello World!' };

Damit ist die Funktion hello nur noch in ImportHello nutzbar, aber nicht mehr von außen:

$ perl -I. -MImportHello -e 'ImportHello->hello()'
Hello World!
Can't locate object method "hello" via package "ImportHello" at -e line 1

Nicht nur Funktionen sind exportierbar

Aber nicht nur Funktionen können so lexikalisch exportiert werden, sondern auch beliebige Variablen:

package ExportAll;
  
use v5.38;

use experimental 'builtin';
use builtin 'export_lexically';

my ($scalar, @array, %hash);
export_lexically 
  '$year'  => \$scalar,
  '@talks' => \@array,
  '%info'  => \%hash,
  check    => sub {
      say "Year: $scalar, Talks: @array"
  }
;

Die Variablen $scalar, @array und %hash werden unter anderem Namen exportiert. Der Code, der dieses Modul nutzt, hat dann die Variablen $year, @talks und %info sowie die Funktion check zur Verfügung. Wichtig bei der Definition ist die Verwendung der einfachen Anführungszeichen.

Wird das Modul ExportAll jetzt verwendet, können die Variablen und die Funktion verwendet werden:

use ExportAll;

$year = 2023;
@talks = qw(Perl_5.38 Playwright);
check();

In dem Skript werden die Variablennamen $year und @talks verwendet, obwohl die an keiner Stelle erstellt wurden. Im Modul ExportAll wird auf die Moduleigenen Variablen $scalar und @array zugegriffen.

Auch bei den Variablen gilt, dass diese lexikalisch sind und von außen nicht darauf zugegriffen werden kann.


Permalink: