Philippe Bruhat (BooK) > perlsecret > perlsecret

Download:
perlsecret-1.011.tar.gz

Dependencies

Annotate this POD

CPAN RT

Open  0
View/Report Bugs
Module Version: 1.011   Source  

NAME ^

perlsecret - Perl secret operators and constants

SYNOPSIS ^

Perl secret operators:

    Operator     Nickname                     Function
    ======================================================
    0+           Venus                        numification
    @{[ ]}       Babycart                     list interpolation
    !!           Bang bang                    boolean conversion
    }{           Eskimo greeting              END block for one-liners
    ~~           Inchworm                     scalar
    ~-           Inchworm on a stick          high-precedence decrement
    -~           Inchworm on a stick          high-precedence increment
    -+-          Space station                high-precedence numification
    =( )=        Goatse                       scalar / list context
    =< >=~       Flaming X-Wing               match input, assign captures
    ~~<>         Kite                         a single line of input
    <<m=~m>> m ; Ornate double-bladed sword   multiline comment
    -=!   -=!!   Flathead                     conditional decrement
    +=!   +=!!   Phillips                     conditional increment
    x=!   x=!!   Pozidriv                     conditional reset to ''
    *=!   *=!!   Torx                         conditional reset to 0
    ,=>          Winking fat comma            non-stringifying fat comma
    ()x!!        Enterprise                   boolean list squash
    0+!!         Key to the truth             numeric boolean conversion
    ||()         Abbott and Costello          remove false scalar from list
    //()         Leaning Abbott and Costello  remove undef from list

Perl secret constants:

    Constant    Nickname                    Value
    ======================================================
    <=><=><=>   Space fleet                 0
    <~>         Amphisbaena                 $ENV{HOME}

DESCRIPTION ^

Perl has a long tradition of giving nicknames to some of its operators (possibly a form of Huffmanisation). These nicknames are based on the appearance of the operator, rather than its function. The well-known examples are the diamond operator (<>), nicknamed by Geneva Wall and the spaceship operator (<=>), nicknamed by Randal Schwartz. Some lesser known Perl operators with a nickname are the fat comma (=>) and yada yada (...).

The Perl "secret operators" have been discovered (or created) by Perl obfuscators and golfers, usually when looking for a shorter way to perform a given operation. Secret operators are not actually secret, and they are not actually operators either. The perl parser does not specifically recognise them, and no one is trying to hide them from you. But they are like operators in the sense that these Perl programmers see them often enough to recognize them without thinking about their smaller parts, and eventually add them to their toolbox. And they are like secrets in the sense that they have to be discovered by their future user (or be transmitted by a fellow programmer), because they are not explicitly described in the Perl core documentation.

Because secret operators are not operators they don't have real names, and so they need nicknames. Like the above Perl operators, their name is usually related to their shape.

The term "secret operator" was probably coined by Abigail in a comp.lang.perl.misc post in January 2003.

A word of warning

Many of those "operators" are not suitable for production code, because they are obscure to the uninitiated, although some are just names for common idioms. The really secret operators are used by golfers, obfuscators and people who like to have fun with their favorite programming language.

You're welcome to try these at home, but they might not be safe for work!

SECRET OPERATORS ^

The following section presents the Perl secret operators, with some historical context, an explanation of how they work and examples of use.

Venus

    0+
    +0

The Venus operator is a name given to a very common idiom. It performs the numification of the value on its right/left, depending of the version used. (This is accomplished by using the identity element for the addition).

    print 0+ '23a';                 # 23

    print 0+ '3.00';                # 3

    print 0+ '1.2e3';               # 1200

    print 0+ '42 EUR';              # 42

    print 0+ 'two cents';           # 0

    $ref = [];
    print 0+ $ref, ' ', "$ref";     # 164094424 ARRAY(0x9c7e1d8)

    print 0+ $!, ' ', $!;           # 2 No such file or directory

Note that 0+ is the method name used for "numeric conversion" by the overload module.

Baby cart

    @{[ ]}

Discovered by Larry Wall, 1994. (Alternate nicknames: "shopping-trolley", "pram", "turtle")

The baby cart operator performs list interpolation inside a string. The list items are separated by the value of $".

    # SQL in a heredoc
    local $" = ',';
    my $sth = $self->execute( << "SQL" );
     SELECT id, name, salary
       FROM employee
      WHERE id IN (@{[ keys %employee ]})
    SQL

    # process args in %arg
    # and spit out the unknown ones
    die "Uuh?: @{[ sort keys %arg ]}\n"

Another use case is for breaking aliasing (i.e. make a copy); for example to avoid the Modification of a read-only value attempted fatal error when running such code:

    for (qw( 1 2 3 )) {
        $_ = $_ * $_;    # contrived
        print "square: $_\n";
    }

With the babycart, the topic is an actually modifiable copied scalar.

    for ( @{[ qw( 1 2 3 ) ]} ) {
        $_ = $_ * $_;    # contrived
        print "square: $_\n";
    }

This is a container, or circumfix operator. The expression inside the [] is run in list context, stored in an anonymous array, which is immediately dereferenced by @{}.

You will see this occasionally in production code.

Bang bang

    !!

This operator was in common usage by C programmers even before Perl existed. It performs boolean conversion, by performing logical negation twice.

    my $true  = !! 'a string';   # now 1
    my $false = !! undef;        # now ''

Eskimo greeting

    }{

(Alternate nickname: "butterfly")

Discovered by Abigail, in 1997.

The eskimo greeting operator is an END block for one-liners.

The following program counts and prints the number of lines in the input:

    $ perl -lne '}{print$.'

The Eskimo greeting abuses the way the -p and -n options generate Perl code (as shown by using the B::Deparse module):

    $ perl -MO=Deparse -lne '}{print$.'
    -e syntax OK
    BEGIN { $/ = "\n"; $\ = "\n"; }
    LINE: while (defined($_ = <ARGV>)) {
        chomp $_;
    }
    {
        print $.;
    }

The discovery was done after The Perl Journal published an interview with Chip Salzenberg, in which he explained what hack perl uses to implement -p and -n.

Ethnographic note: in modern Western culture, an eskimo kiss is the act of pressing the tip of one's nose against another's. It is loosely based on a traditional Inuit greeting called a kunik, that early explorers of the Arctic dubbed "Eskimo kissing" when they first witnessed it. The kunik itself is not a kiss, not erotic, and simply of form of affectionate greeting.

Inchworm

    ~~

This operator is basically a shorter scalar (shaves 4 characters!) using the same idea as the secret bang bang operator.

    $ perl -Esay~~localtime
    Tue Mar 13 19:53:25 2012

The inchworm looks very much like the smart-match operator introduced in Perl 5.10, but since it's actually a sequence of two unary operators, the Perl parser can't mix it up with the binary smart-match.

Note that Perl's ~ is operand sensitive: if its operand has a numeric value (either because it was assigned a number, the result of a numeric operation, or had been used in numeric context), it is a numeric bitwise negation (first implicitly converting to unsigned integer (UV), or under the scope of use integer, signed integer (IV)); otherwise it is a string bitwise negation.

And this explains how it differs from !!. Instead of forcing the operand into some kind of boolean, it forces it into some kind of either string or number (depending on the operand).

Thus, for most inputs, the inchworm acts just like scalar().

Examples of exceptions:

    # floating point
    $x = 1.23;
    print ~~$x;                # 1

    # string used in numeric context
    $x = "1.23";
    print ~~$x if $x != 0;     # 1

    # integer out of range
    use Config '%Config';

    $x = 2**( 8 * $Config{uvsize} );
    print ~~$x;                # UV_MAX

    $x = -1;
    print ~~$x;                # UV_MAX

    $x = 2**( 8 * $Config{uvsize} - 1 );
    {
        use integer;
        print ~~$x;            # IV_MIN
    }

    $x = -2**( 8 * $Config{uvsize} - 1 ) - 1;
    {
        use integer;
        print ~~$x;            # IV_MIN
    }

But it is also handy as a shorthand to get stringification from objects that overload it in some useful way:

    use DateTime;
    use JSON;
    my $now = DateTime->now;
    print encode_json { time => ~~$now };

Inchworm on a stick

    ~-
    -~

Discovered by Ton Hospel, 2002.

These two operators perform a high-precedence decrement (~-) and high-precedence increment (-~) on integers (on a two's-complement architecture). It's a trick that assembly language programmers have been using for decades.

In C, Python and Ruby, they work on all integers. Due to how ~ is implemented in Perl (a little known fact is that Perl's bitwise operators cast operands to unsigned integers without use integer and to signed integers with use integer), this pair of secret operators is limited to signed integers. It's actually even more limited by default: ~- only decrements integers greater than 0, and -~ only increments integers lesser than 0. To get the inchworms on a stick to work on all signed integers, they must be used under the scope of use integer, so that signed integers are used everywhere in bitwise operations.

This golfing technique allows to get rid of a pair of parentheses:

    $y = ~-$x * 4;    # identical to $y = ($x-1)*4;

Here's the proof:

    $x - 1 == - ( -$x ) - 1

In two's complement architectures, to get the opposite of a number, all you need to do is flip all bits, and add 1. I.e.,

    -$i == ~$i + 1

Using this to replace - ( -$x ) in the above identity, we get:

    $x - 1 == ( ~-$x + 1 ) - 1

And after eliminating the ones from the equation,

    $x - 1 == ~-$x

QED.

For -~, the proof is similar:

    $x + 1 == - ( -$x ) + 1

    $x + 1 == - ( ~$x + 1 ) + 1

    $x + 1 == -~$x - 1 + 1

    $x + 1 == -~$x

In both versions, the high precedence comes from the fact that ~ and unary - both have higher precedence than all other arithmetic operators (except **).

Mnemonic: the backwards-facing inchworm on a stick (~-) decrements, and the forward-facing inchworm on a stick (-~) increments.

Space station

    -+-

Discovered by Alistair McGlinchy, 2005.

This operator performs a high precedence numification.

    print -+- '23a';                # 23

    print -+- '3.00';               # 3

    print -+- '1.2e3';              # 1200

    print -+- '42 EUR';             # 42

    $ref = [];
    print -+- $ref, ' ', "$ref";    # 151097816 ARRAY(0x90191d8)

    print -+- $!, ' ', $!;          # 2 No such file or directory

At first, this looks exactly like the Venus operator. However, because the Venus operator uses a binary +, it has a lower precedence than the multiplicative operators like * or x. On the other hand, the space station operator is the concatenation of three unary operators, and therefore has higher precedence.

In the following example, we'll try to print the numification of the string '20GBP' (i.e. '20') repeated three times.

    # wrong: prints the numification of '20GBP20GBP20GBP'
    print 0+ '20GBP' x 3;           # 20

    # wrong: does the equivalent of ( print '20' ) x 3
    print( 0+ '20GBP' ) x 3;        # 20

    # right: but too lengthy, too lispy
    print( ( 0 + '20GBP' ) x 3 );   # 202020

    # right: uses the space station operator
    print -+- '20GBP' x 3;          # 202020

However, because unary minus simply replace the initial - or + of a string by its counterpart, the space station does not numify strings starting with a minus or strings that do not start with a number:

    print -+- 'two cents';          # +two cents

    print -+- '-2B' x 5;            # -2B-2B-2B-2B-2B

In the above example, -+- '-2B' produces the string '-2B', whereas 0+ '-2B' would have given the expected number (-2).

Goatse

    =( )=

(Alternate nickname: "Saturn")

If you don't understand the name of this operator, consider yourself lucky. You are advised not to search the Internet for a visual explanation.

The goatse operator provides a list context to its right side and returns the number of elements to its left side. Note that the left side must provide a scalar context; obviously, a list context on the left side will receive the empty list in the middle.

The explanation is that a list assignment in scalar context returns the number of elements on the right-hand side of the assignment, no matter how many of those elements were actually assigned to variables. In this case, all the elements on the right are simply assigned to an empty list (and therefore discarded).

    # count the words in $_
    $n =()= /word1|word2|word3/g;

    # $n = 1
    $n =()= "abababab" =~ /a/;

    # $n = 4
    $n =()= "abababab" =~ /a/g;

The goatse operator is a container (sic), so it can also be used to assign values from the right-hand side to the variables inside it.

    # $n = 4; $b = 'a'
    $n =($b)= "abababab" =~ /a/g;

    # $n = 4; @c = qw( a a a a )
    $n =(@c)= "abababab" =~ /a/g;

In some cases, the full goaste is not needed, because there is no need to store the value in a variable. The side-effect of list assignment in scalar context can be obtained with a right-handed goaste (()=) used in a scalar context provided by some other mean than assignement to a scalar. For example:

    # remove empty array refs
    @arrays = ( [], [1], [ 1, 2 ], [], [ 5 .. 9 ] );

    # @filled = ( [1], [ 1, 2 ], [ 5 .. 9 ] );
    @filled = grep +()= @$_, @arrays;

(The + is in the above line is a no-op, used to tell grep that the parentheses are not enclosing its arguments.)

Here's a convoluted example where =()= seems to be the proper construct to use, but it's actually another secret operator that really does the trick.

Imagine you want to know in how many elements split() would split a string, but do not care about the elements themselves. Using split() in scalar context:

    my $count = split /:/, $string;

Gives the correct answer, but also a warning:

    Use of implicit split to @_ is deprecated

Using =()= to force scalar context on the left side (to get the number of substrings) and list context on the right side (to avoid the deprecated not-in-list-context construct) seems like the proper solution:

    my $count =()= split /:/, $string;

It does not warn indeed, but always returns 1 (which is usually wrong).

The reason is that split() never splits to more fields than necessary. And the compiler interprets storing the results in () as not caring about the results, so split() will not split the string at all, and thus return the full string, which gives a list of only one element in scalar context, hence the 1.

You have two options to address this. First, you can override split()'s optimization by explicitly asking it to split into an unlimited number of fields:

    my $count =()= split /:/, $string, -1;

Or else you can defeat the optimization by using another secret operator instead, the baby cart:

    my $count = @{[ split /:/, $string ]};

This causes split() to detect that its caller can store any number of fields, thus it will actually do the work before the anonymous array is thrown away after being used in scalar context.

Flaming X-Wing

     =<>=~

Discovered by Philippe Bruhat, 2007.

This operator applies a regular expression to a single line of input and assigns the captured values to the expression to its left-hand side.

    # pick named fields from input
    @data{@fields} =<>=~ $regexp;

The above statement decomposes as follows: =~ provides scalar context to <> on its left, thus matching on a single line of input. If the regular expression contains captures, having an array-like structure on the left side of the = provides list context, and the captured data is assigned to the structure.

This operator is also a container. So the X-Wing can have a pilot!

    # use the source, Luke!
    $luke = \*DATA;
    @data{@fields} =<$luke>=~ $regexp;

Kite

     ~~<>

Discovered by Philippe Bruhat, 2012. (Alternate nickname: "sperm")

This operator is actually a combination of the inchworm and the diamond operator. It provides scalar context to the readline() builtin, thus returning a single line of input.

It's only useful in list context (since <> already returns a single line of input in scalar and void contexts), for example for getting several lines at once:

    @triplets = ( ~~<>, ~~<>, ~~<> );    # three sperms in a single egg?

Like the other operators based on bracketing constructs, the kite is a container, and can carry a payload (a file handle, in this case).

Note that when the filehandle is exhausted, the kite operator will return the empty string instead of undef.

Mnemonic: It provides a feature that is tied to one line, a string, as it were. (Tye McQueen in http://www.perlmonks.org/?node_id=959906).

Ornate double-bladed sword

    <<m=~m>>
    m
    ;

Created by Abigail, 2003, for comp.lang.perl.misc.

This operator provides multi-line comments, by clever use of heredoc syntax and beautiful symmetry. Quoting <slrnb382jc.tfm.abigail@alexandra.abigail.nl>:

    <<m=~m>>
      Use the secret operator on the previous line.
      Put your comments here.
      Lots and lots of comments.

      You can even use blank lines.
      Finish with a single
    m
    ;

The "ornament" is the m ribbon with the ; throwing blade attached to it.

Note that the "commented" text is actually a double-quoted string in void context, which can have some side effects.

Screwdriver operators

Discovered by Dmitry Karasik, 2007, while looking for !-based operators.

All screwdriver operators are conditional operators. Like screwdrivers, they come in 4 major types, with different handle lengths.

Mnemonic: the screwdriver's head is the best mnemonic (- and + for increment and decrement, * for the null number, x for the null string).

Winking fat comma

    ,=>

Discovered by Abigail, 2010. (Alternate nickname: "grappling hook")

Visually looks like a fat comma, but without the left-hand side behaviour.

This operator is used to retain the documenting features of the fat comma, while disabling the string interpretation of the word to its left.

    use constant APPLE   =>  1;
    use constant CHERRY  =>  2;
    use constant BANANA  =>  3;

    %hash = (
      APPLE   ,=>  "green",
      CHERRY  ,=>  "red",
      BANANA  ,=>  "yellow",
    );

is equivalent to:

    %hash = ( 1, "green", 2, "red", 3, "yellow" );

Mnemonic: the comma , is an off-switch for the fat comma's stringification.

Enterprise

    ( )x!!

Discovered by Aristotle on PerlMonks, 2006. (Alternate nicknames: "NCC-1701", "snail")

It is often necessary to conditionally include items in a list:

    my @shopping_list = ('bread', 'milk');
    push @shopping_list, 'apples'   if $cupboard{apples} < 2;
    push @shopping_list, 'bananas'  if $cupboard{bananas} < 2;
    push @shopping_list, 'cherries' if $cupboard{cherries} < 20;
    push @shopping_list, 'tonic'    if $cupboard{gin};

The Enterprise lets you build the list and add only the items that match the condition, in a single statement:

    my @shopping_list = (
        'bread',
        'milk',
       ('apples'   )x!! ( $cupboard{apples} < 2 ),
       ('bananas'  )x!! ( $cupboard{bananas} < 2 ),
       ('cherries' )x!! ( $cupboard{cherries} < 20 ),
       ('tonic'    )x!! $cupboard{gin},
    );

This operator is a container, which means the Enterprise can have a large crew.

The Enterprise is simply a list repetition operator ()x followed by a boolean (see the "Bang bang" operator above) which will be interpreted as 1 or 0 in a numeric context. Note that the expression on the left is always evaluated, regardless of the state of the condition.

Because of precedence issues, complex conditions on the tail of the Enterprise may need to be put between parentheses.

Key to the truth

    0+!!

Discovered by Toby Inkster, 2013.

This operator is a combination of the Venus and bang bang operators. It simply makes the boolean false into the 0 numeric value.

    my $true  = 0+!! 'a string';    # now 1
    my $false = 0+!! undef;         # now 0

Abbott and Costello

    ||()

Discovered by Yves Orton.

This operator makes a false value disappear in list context. It simply replaces a false scalar by the empty list.

    my @shopping_list = (
        'bread',
        'milk',
        $this ||(),
        $that ||(),
        'apples'
    );

Mnemonic: one is tall (and also the "straight man") and one is fat, just like the famous comedy duo.

Leaning Abbott and Costello

    //()

Proposed by Damien Krotkine, 2014.

This operator works exactly like the "Abbott and Costello", except that it only makes undef disappear in list context.

This operator only works in Perl versions above 5.10.

Mnemonic: in this version, the "straight man" is leaning (and so the associativity is really "(leaning Abbott) and Costello").

SECRET CONSTANTS ^

Space fleet

    <=><=><=>

Discovered by Damian Conway.

Even though it looks like a sequence of three spaceship operators, only the middle ship is an actual spaceship. The two outer "spaceships" are actually calls to glob("=").

This constant has the value 0.

Amphisbaena

    <~>

Discovered by Rafaël Garcia-Suarez, 2009.

Under Unix, will be equal to the real user home directory (by using glob). On Win32 it will expand to $ENV{HOME} if it is set (which is quite uncommon) or return '~' else.

AUTHOR ^

Philippe Bruhat (BooK)

ACKNOWLEDGMENTS ^

The author would like to thank José Castro, Andrew Savige, Dmitry Karasik, Abigail, Yitzchak Scott-Thoennes, Zefram, Tye McQueen, Maxim Vuets, Aristotle Pagaltzis, Toby Inkster, Ævar Arnfjörð Bjarmason, Rafaël Garcia-Suarez, Andreas J. König, Andy Armstrong, Pau Amma, Keith C. Ivey, Michael R. Wolf, Olivier Mengué, Yves Orton, Damien Krotkine, Diab Jerius, Ivan Bessarabov and the Fun With Perl mailing list for inspiration, suggestions and patches.

CONTRIBUTING ^

If you know of other secret operators or constants, please let me know!

However, before sending me your latest creation, be aware that, although these are not set in stone, I consider the following criteria before adding an operator or constant to this list:

REFERENCES ^

As shown below, most of the secret operator action happens on the Fun With Perl mailing-list.

More secret operators didn't make it to this list, because they don't have a name yet.

COPYRIGHT ^

Copyright 2010-2014 Philippe Bruhat (BooK).

LICENSE ^

This documentation is free; you can redistribute it and/or modify it under the same terms as Perl itself.

syntax highlighting: