Blog

Auf dem Weg zu Perl 5.36 - builtin

20.05.2022 // Renée Bäcker

Mit Perl 5.36 gibt es ein neues Pragma: builtin. Damit lassen sich neue Hilfsfunktionen in das Skript/Modul importieren. Derzeit bietet das Pragma folgende Hilfsfunktionen:

  • true, false, isbool
  • weaken, unweaken, isweak
  • blessed
  • refaddr, reftype
  • ceil, floor
  • trim

Booleans

use v5.35;
 
use builtin qw(true false is_bool);
no warnings 'experimental::builtin';
 
my $true = true;
my $false = false;
 
say is_bool( $true );
say is_bool( $false );

Aktuell ist das Pragma als experimentell eingestuft. Beim Einbinden des Pragmas muss angegeben werden, welche Funktionen importiert werden sollen (alternativ kann man bei den Aufrufen einfach den vollqualifizierten Namen angeben, z.B. builtin::true().

true und false erstellt jeweils Werte, die im boolschen Kontext wahr bzw. unwahr liefern. In Strings und/oder Berechnungen werden aber die Standardwerte für wahr/*unwahr* genommen: 1 bzw. "" (Leerstring).

Es gibt einen Unterschied zu den Werten, die man bisher typischerweise für die boolschen Werte verwendet hat: In den Interna der Variablen wird nun zur Laufzeit gespeichert, dass es sich um boolsche Werte handelt:

use Devel::Peek;

my $true = builtin::true;
my $false = builtin::false;

Dump( $true ); Dump( $false );

Liefert

SV = PVNV(0x5653938f9380) at 0x56539391f968
  [...]
  PV = 0x565392196bc4 "1" [BOOL PL_Yes]
  [...]
SV = PVNV(0x5653938f93a0) at 0x56539391f938
  [...]
  PV = 0x565392196bc3 "" [BOOL PL_No]
  [...]

Man sieht hier den Zusatz [BOOL PL_{Yes|No}]. Diese Zusatzinformation wird auch von builtin::is_bool ausgewertet. Damit kann man unterscheiden, ob die 1 ein boolscher Wert ist oder einfach die Zahl 1:

my $zahl = 1;
my $wahr = builtin::true;

print sprintf '$zahl ist bool: %s, $wahr ist bool: %s', 
    builtin::is_bool( $zahl ),
    builtin::is_bool( $wahr );

mit der Ausgabe

$zahl ist bool: , $wahr ist bool: 1

Referenzen

Auf Referenzen arbeiten einige Hilfsfunktionen:

  • weaken, unweaken, isweak
  • blessed
  • refaddr, reftype

Diese Funktionen sind auch in Scalar::Util verfügbar, deshalb zeige ich hier nur die – in meinen Augen – wichtigsten Funktionen weaken, blessed und reftype.

Mit blessed kann man herausfinden, ob eine Variable eine ge*blessed*e Referenz (sprich ein Objekt) ist. Ist das der Fall, wird der entsprechende Paketname zurückgeliefert:

use builtin qw(blessed);
no warnings 'experimental::builtin';

use Math::BigInt;
 
my $int = Math::BigInt->new(2022);
 
if ( blessed $int ) {
    say '$int is blessed';
    say "package: ", blessed $int;
}

liefert

$ perl blessed.pl 
$int is blessed
package: Math::BigInt

Mit reftype bekommt man den Namen der Datenstruktur, auf die die Referenz verweist. Die meisten werden die Funktion ref kennen:

my $array_ref = [];
say ref( $array_ref ); # liefert ARRAY

Soweit so gut. Sobald es aber ein Objekt ist, wird nicht der Name der Datenstruktur ausgegeben, sondern der Name des Pakets:

my $array_object = bless [], 'Array';
say ref( $array_object ); # liefert Array, also den Paketnamen

reftype liefert in jedem Fall den Namen der Datenstruktur:

my $array_object = bless [], 'Array';
say reftype( $array_object ); # liefert ARRAY

Werden komplexe Datenstrukturen aufgebaut, kann es schnell passieren, dass man zirkuläre Referenzen aufbaut:

my %hash_eins;
my %hash_zwei = ( eins => \%hash_eins );
$hash_eins{zwei} = \%hash_zwei;

Damit hat man ein Speicherleck erzeugt. Perl nutzt einen Referenzzähler, um Speicher freizugeben. Und der kann hier nie auf 0 gehen, da in einer zirkulären Referenz immer A auf B verweist und B auf A.

In so einem Fall kann man weaken nutzen. Damit wird der Referenzzähler der Variable nicht hochgezählt:

my %hash_eins;
my %hash_zwei = ( eins => \%hash_eins );

$hash_eins{zwei} = \%hash_zwei;
builtin::weaken( $hash_eins{zwei} );

Zu diesem Problem werden wir noch einen weiteren Blogartikel schreiben.

Endlich trimmen

Eine kleine Funktionalität, die aber auch bei den Entwicklern von Perl viel Diskussionen hervorgerufen hat. Es gibt auf CPAN etliche Implementierungen davon. Leerzeichen am Anfang und am Ende eines Strings entfernen.

Warum gab es diese Diskussionen? Es ging hauptsächlich darum, was das trimmen genau machen soll. Einfach nur Leerzeichen oder noch andere Whitespaces? Soll der Nutzer die Möglichkeit haben, die zu entfernenden Zeichen selbst festzulegen? Soll das ganze Inplace (wie s///) passieren oder soll der veränderte Wert zurückgegeben werden?

Mit trim werden die Whitespaces am Anfang und am Ende eines Strings entfernt und der veränderte Wert wird zurückgegeben:

use builtin qw(blessed);
no warnings 'experimental::builtin';

my $t = "\t   \ntest    \n    "; 
say ">>",$t,"<<"; 

my $trimmed = builtin::trim($t); s
ay ">>$trimmed<<"

liefert die Ausgabe

>>     
test    
    <<
>>test<<

Im Laufe der Zeit werden sicher noch etliche nützliche Hilfsfunktionen im builtin::-Namensraum landen. Schon in Perl 5.36 sind viele Helferlein enthalten, die schon so oft vermisst wurden. Die Vorfreude auf das neue Release wächst.



Permalink: