How to Identify a Good Perl Programmer

by alba ferrer - @dawnlua

cc-by-sa

$me

  • Started with Perl when I started working as sysadmin
  • Found my current job in the Barcelona Perl Mongers list
  • Not a developer, but need to know how to program
  • Read the books, attended YAPCs,... but you need to program in order to learn!
  • Best way to know if you know, is to teach
  • Perltuts

$this_talk

  • Modern Perl Programming blog post
  • January 2011
  • chromatic
Here's a list of questions every good Perl programmer should be able to answer effectively:

What do variable sigils indicate in Perl 5?

In computer programming, a sigil (/ˈsɪdʒəl/) is a symbol attached to a variable name, showing the variable's datatype or scope, usually a prefix, as in $foo, where $ is the sigil.
Sigil, from the Latin sigillum, meaning a "little sign", means a sign or image supposedly having magical power.[1] In 1999 Philip Gwyn adopted the term "to mean the funny character at the front of a Perl variable".

Wikipedia - Sigil

  • Indicate the type or 'container' of that variable
  • There are three common types of sigils:
    • $: scalar
    • @: array
    • %: hash

What's the difference between accessing an array element with $items[$index] and @items[$index]?


my @jogadoras = ('cegonha', 'sol', 'colorada', 'creta', 'lua', 'macaquinha');

say "With \@ we get: @jogadoras[2]";
say "With \$ we get: $jogadoras[2]";
						

Scalar value @jogadoras[2] better written as $jogadoras[2] at test.pl line 9.
With @ we get: colorada
With $ we get: colorada
						

@items[$index] used to be a way to do a single item array slice, but it's not used anymore.

What's the difference between == and eq?

== forces the comparison in numeric context

eq forces the comparison in string context


if ( "gunga" eq "médio" ) {
    say "[eq] Gunga and médio are the same type of berimbau";
} else {
    say "[eq] Gunga and médio are different types of berimbau";
}

if ( "gunga" == "médio" ) {
    say "[==] Gunga and médio are the same type of berimbau";
} else {
    say "[==] Gunga and médio are different types of berimbau";
}

						

[eq] Gunga and médio are different types of berimbau
Argument "médio" isn't numeric in numeric eq (==) at test.pl line 13.
Argument "gunga" isn't numeric in numeric eq (==) at test.pl line 13.
[==] Gunga and médio are the same type of berimbau
						

  DB<1> $berimbau = 'gunga'

  DB<2> x int $berimbau
0  0
  DB<3> $berimbau = '2gunga'

  DB<4> x int $berimbau
0  2
  DB<5> $berimbau = 'gunga2'

  DB<6> x int $berimbau
0  0
						

What do you get if you evaluate a hash in list context?


my %berimbaus = (
    'gunga' => 'leader',
    'médio' => 'invert',
    'viola' => 'free style'
);

my @types_of_berimbaus = %berimbaus;

say Dumper @types_of_berimbaus;
						

$VAR1 = 'médio';
$VAR2 = 'invert';
$VAR3 = 'gunga';
$VAR4 = 'leader';
$VAR5 = 'viola';
$VAR6 = 'free style';
						

We get a list of key-value pairs. Every pair in the hash becomes two elements in the list.

How do you look up keywords in the Perl documentation?

perldoc -f «keyword»

if «keyword» is a function

perldoc -q «keyword»

if «keyword» is any word, to look it up in the FAQ

What is the difference between a function and a method in Perl 5?

The way they are used

Function Method
Class::Name::function($param) $object->method($param) Class::Name->method

The way they are looked up

Function Method
Perl will look for it in the current class only Perl will look for it in the current class, then its parents, then their parents... until stopping or calling AUTOLOAD

The parameters they get

Function Method
Whatever you pass explicitly First parameter is always the current object, then whatever you pass explicitly

When does Perl 5 reclaim the memory used by a variable?

When it disappears (end of scope, function,...)

How do you ensure that the default scope of a variable is lexical?

local $x: saves away the old value of the global variable $x and assigns a new value for the duration of the subroutine/scope (and the subroutines called from it).

my $x: creates a new variable that is visible only in the current subroutine/scope.

Perldoc FAQ7


our $var = 'global';

sub visible {
    print "var has value $var\n";
}
sub dynamic {
    local $var = 'local';
    visible();
}
sub lexical {
    my $var = 'private';
    visible();
}

visible();
dynamic();
lexical();
						

var has value global
var has value local
var has value global
						
By using my

How do you load and import symbols from a Perl 5 module?

In order to import, you first need to export

Export


	package Capoeira;
	use Exporter 'import'; # gives you Exporter's import() method directly
  	@EXPORT_OK = qw(ginga queixada au_sem_maos); # symbols to export on request
						

Import


	use Capoeira qw(ginga queixada);
						

How can you influence the list of directories from which perl attempts to load modules?

@INC: contains the list of places that do, require or use constructs look for their library files.

  • Arguments to any -I command line switches
  • Default Perl library (e.g. /usr/local/lib/perl)
  • .

BEWARE!!!
This behavior changes in 5.26, and '.' is not added anymore (security vulnerability)

To load modules from other directories, use the use lib pragma


	use lib '/home/alba/myhobbies/capoeira/lib/';
	use Capoeira;
						

How do you look up error messages in the Perl 5 documentation?

(Award bonus points for knowing how to enable explanations of all error messages encountered)

diagnostics and splain


	use diagnostics;
	enable diagnostics;
						

	perl jogo.pl 2>diag.out
	splain diag.out
						

	Global symbol "$roda" requires explicit package name at jogo.pl line 7 (#1)
    (F) You've said "use strict" or "use strict vars", which indicates
    that all variables must either be lexically scoped (using "my" or "state"),
    declared beforehand using "our", or explicitly qualified to say
    which package the global variable is in (using "::").

    Global symbol "$roda" requires explicit package name at jogo.pl line 8 (#1)

    Execution of jogo.pl aborted due to compilation errors (#1) (#2)

						

What happens when you pass an array into a function?

The array is "flattened" and every element becomes a parameter in the function


sub jogar {
        my @participantes = shift;
        say "As participantes na roda sao " . join(",", @participantes);
}

my @jogadoras = ('cegonha', 'sol', 'colorada', 'creta', 'lua', 'macaquinha');
jogar(@jogadoras);

						

As participantes na roda sao cegonha
						

How do you pass one or more distinct arrays into a function?

By passing them as references


sub jogar {
        my $jogadoras = shift;
	my $jogadores = shift;
       	say "As participantes na roda sao " . join(",", @$jogadoras);
        say "Os participantes na roda sao " . join(",", @$jogadores);
}

my @jogadoras = ('cegonha', 'sol', 'colorada', 'creta', 'lua', 'macaquinha');
my @jogadores = ('marujo', 'tarzan', 'socorrista', 'lambarí', 'afogado', 'calado');
jogar(\@jogadoras, \@jogadores);
						

As participantes na roda sao cegonha,sol,colorada,creta,lua,macaquinha
Os participantes na roda sao marujo,tarzan,socorrista,lambarí,afogado,calado
						

What is the difference, on the caller side, between return; and return undef;?

In scalar context


use Data::Dumper;

sub return_nothing {
        return;
}

sub return_undef {
        return undef;
}

my $res1 = return_nothing();
my $res2 = return_undef();

say Dumper($res1);
say Dumper($res2);
						

$VAR1 = undef;

$VAR1 = undef;
						

In list context


use Data::Dumper;

sub return_nothing {
        return;
}

sub return_undef {
        return undef;
}

my @res1 = return_nothing();
my @res2 = return_undef();

say Dumper(\@res1);
say Dumper(\@res2);
						

$VAR1 = [];

$VAR1 = [
          undef
        ];
						

Where do tests go in a standard CPAN distribution?

In the t directory, at the root of the distribution

alba@aeryn:~/codi/Paws-0.28$ ls
bin       dist.ini        lib          MANIFEST   old_t      TODO
Changes   examples        LICENSE      META.json  README.md  TODO_in_branch
cpanfile  gen_classes.pl  Makefile.PL  META.yml   t          xt
						

alba@aeryn:~/codi/Paws-0.28/t$ ls
01_load.t                  11_client_exceptions_httptiny
02_aws_object.t            11_client_exceptions_lwp
03_aws_default_object.t    11_client_exceptions.t
04_credentials             12_regions.t
04_credentials.t           13_client_connect_errors.t
05_service_calls.t         14_dns_client_errors.t
10_generate_test_cases.pl  15_timeouts.t
10_responses               16_retries.t
10_responses.t             lib
11_client_exceptions_furl
						

How do you run tests in a standard CPAN distribution?

With the prove command


prove - Run tests through a TAP harness.
						

alba@aeryn:~/codi/Paws-0.28$ prove -l/ -I local/lib/perl5/ t/01_load.t
t/01_load.t .. 1/? Paws::ApiGateway is not stable / supported / entirely developed at /home/alba/codi/Paws-0.28/lib/Paws/ApiGateway.pm line 2,  line 1.
t/01_load.t .. 5/? Paws::CloudFront is not stable / supported / entirely developed at /home/alba/codi/Paws-0.28/lib/Paws/CloudFront.pm line 2,  line 1.
Paws::CloudSearchDomain is not stable / supported / entirely developed at /home/alba/codi/Paws-0.28/lib/Paws/CloudSearchDomain.pm line 2,  line 1.
t/01_load.t .. 15/? Paws::CognitoSync is not stable / supported / entirely developed at /home/alba/codi/Paws-0.28/lib/Paws/CognitoSync.pm line 2,  line 1.
t/01_load.t .. 29/? Paws::EFS is not stable / supported / entirely developed at /home/alba/codi/Paws-0.28/lib/Paws/EFS.pm line 2,  line 1.
t/01_load.t .. 33/? Paws::ES is not stable / supported / entirely developed at /home/alba/codi/Paws-0.28/lib/Paws/ES.pm line 2,  line 1.
t/01_load.t .. 37/? Paws::ElasticTranscoder is not stable / supported / entirely developed at /home/alba/codi/Paws-0.28/lib/Paws/ElasticTranscoder.pm line 2,  line 1.
t/01_load.t .. 41/? Paws::Glacier is not stable / supported / entirely developed at /home/alba/codi/Paws-0.28/lib/Paws/Glacier.pm line 2,  line 1.
t/01_load.t .. 43/? Paws::IoT is not stable / supported / entirely developed at /home/alba/codi/Paws-0.28/lib/Paws/IoT.pm line 2,  line 1.
t/01_load.t .. 46/? Paws::IoTData is not stable / supported / entirely developed at /home/alba/codi/Paws-0.28/lib/Paws/IoTData.pm line 2,  line 1.
Paws::Lambda is not stable / supported / entirely developed at /home/alba/codi/Paws-0.28/lib/Paws/Lambda.pm line 2,  line 1.
t/01_load.t .. 57/? Paws::Route53 is not stable / supported / entirely developed at /home/alba/codi/Paws-0.28/lib/Paws/Route53.pm line 2,  line 1.
t/01_load.t .. 59/? Paws::S3 is not stable / supported / entirely developed at /home/alba/codi/Paws-0.28/lib/Paws/S3.pm line 2,  line 1.
t/01_load.t .. ok
All tests successful.
Files=1, Tests=74, 22 wallclock secs ( 0.03 usr  0.00 sys + 21.75 cusr  0.28 csys = 22.06 CPU)
Result: PASS
						

What command do you use to install new distributions from the CPAN?

Linux


#cpan
cpan Capoeira::Jogador;
#cpanminus
cpanm Capoeira::Jogador;
						

Windows


#Strawberry Perl
cpan
cpan> install Capoeira::Musica;
#Active Perl
ppm
ppm> install Capoeira::Musica;
						

Linux - distros packages


#debian-based
apt-get install libcapoeira-roda-perl
#redhat-based
yum install perl-Capoeira-Roda
						

carton


#cpanfile
requires 'Moose';
requires 'Capoeira::Roda';
requires 'Capoeira::Musica';
requires 'Capoeira::Jogador';
requires 'Capoeira::Roda';
						

carton install
						

Why would you use the three-argument form of the open builtin?


open(FILE, "</path/to/file.txt")
						

vs


open(my $fh, "<", "/path/to/file.txt")
						
  • Global variables like FILE can generate problems if they are overwritten somewhere else
  • It's insecure

my $filename = shift;
open my $fh, ">$filename" or die "Can't write to '$filename': $!\n";

say $fh "Born to run\nBobbie Jean\nThunder road";
						

alba@aeryn:~/codi/goodperl$ cat cantigas.txt
No terreiro da fazenda
Malicia e manha
Angola que me leva
Acende o candieiro
Mas que saudade
Deixa o berimbau falar
						

alba@aeryn:~/codi/goodperl$ perl sing.pl cantigas.txt
alba@aeryn:~/codi/goodperl$ cat cantigas.txt
Born to run
Bobbie Jean
Thunder road
						

alba@aeryn:~/codi/goodperl$ perl sing.pl '> cantigas.txt'
alba@aeryn:~/codi/goodperl$ cat cantigas.txt
No terreiro da fazenda
Malicia e manha
Angola que me leva
Acende o candieiro
Mas que saudade
Deixa o berimbau falar
Born to run
Bobbie Jean
Thunder road
						

my $filename = shift;
open my $fh, "$filename" or die "Can't write to '$filename': $!\n";

say $fh "Born to run\nBobbie Jean\nThunder road";
						

alba@aeryn:~/codi/goodperl$ perl sing.pl 'rm -f cantigas.txt |'
						

Use the three-argument open


my $filename = shift;
open my $fh, '>', $filename or die "Can't write to '$filename': $!";
						

alba@aeryn:~/codi/goodperl$ perl sing.pl 'touch malicious_cantigas.sh |'
alba@aeryn:~/codi/goodperl$ ls
sing.pl		cantigas.txt 	touch malicious_cantigas.sh |
alba@aeryn:~/codi/goodperl$ cat touch\ malicious_cantigas.sh\ \|
Born to run
Bobbie Jean
Thunder road
						

How do you detect (and report) errors from system calls such as open?

(Award bonus points for knowing how to enable automatic detection and reporting of errors)

By checking the return code


my $songs = "./cantigas.txt";
my $res = open (my $fh, "<", $songs);
say Dumper($res);
say "$!";
						

$VAR1 = undef;

No such file or directory
						

How do you throw an exception in Perl 5?

Do the call or die


my $songs = "./cantigas.txt";
open (my $fh, "<", $songs) or die "Cannot open file: $!";
						

alba@aeryn:~/codi/goodperl$ perl sing.pl
Cannot open file: No such file or directory at sing.pl line 6.
						

autodie


use autodie;
my $songs = "./cantigas.txt";
open(my $fh, "<", $songs); # No need to check!
						

alba@aeryn:~/codi/goodperl$ perl sing.pl
Can't open './cantigas.txt' for reading: 'No such file or directory' at test.pl line 7
						

How do you catch an exception in Perl 5?

eval

eval BLOCK

[...]the code within the BLOCK is parsed only once--at the same time the code surrounding the "eval" itself was parsed--and executed within the context of the current Perl program.

If there is a syntax error or runtime error, or a "die" statement is executed, "eval" returns "undef" in scalar context or an empty list in list context, and $@ is set to the error message.


my @participantes = ('cegonha', 'lambari', 'calado', 'marujo', 'macaquinha');
eval{
    die "Not enough people for a roda!" unless (@participantes > 10);
};
say "$@ Let's go for a beer instead" if $@;
						

Not enough people for a roda! at jogo.pl line 9.
 Let's go for a beer instead
						

Another option: use try-catch modules

What is the difference between reading a file with for and with while?


open (my $fh, '<', 'cantigas.txt') or die "Cannot read file: $!";

while (my $line = <$fh>) {
    print "$line";
}
						

open (my $fh, '<', 'cantigas.txt') or die "Cannot read file: $!";

foreach my $line (<$fh>) {
    print "$line";
}
						

for will read the whole file and load it in memory
while will read line by line as the loop iterates

  • while is better for big files
  • while is the way to go for growing files (e.g. live logs)
  • For all the rest, they're equivalent

How do you handle parameters within a method or function in Perl 5?

Parameters are kept in the special variable @_

You can handle the parameters by running shift on @_ (implicit variable in the function)


sub jogar {
        my $participantes = shift;
        say "As participantes na roda sao " . join(",", @$participantes);
}

my @jogadoras = ('cegonha', 'sol', 'colorada', 'creta', 'lua', 'macaquinha');
jogar(\@jogadoras);
						

What do parentheses around the variable name in my ($value) = @_; mean, and what would happen if you removed them?

With the parentheses, evaluation is in list context. $value will get the first element of the @_ array

Without the parentheses, evaluation is in scalar context. $value will get the number of elements in the @_ array


sub jogar_par {
        my ($participantes) = @_;
        say $participantes;
}

sub jogar_no_par {
        my $participantes = @_;
        say $participantes;
}

my @jogadoras = ('cegonha', 'sol', 'colorada', 'creta', 'lua', 'macaquinha');
jogar_par(@jogadoras);
jogar_no_par(@jogadoras);
						

cegonha
6
						

Is new a builtin function/keyword?

No

It's just a convention

How do you read the documentation of a core library? A CPAN module?

With perldoc


perldoc Paws
						

NAME
    Paws - A Perl SDK for AWS (Amazon Web Services) APIs

SYNOPSIS
      use Paws;
      my $obj = Paws->service('...');
      my $res = $obj->MethodCall(Arg1 => $val1, Arg2 => $val2);
      print $res->AttributeFromResult;

DESCRIPTION
    Paws is an attempt to develop an always up-to-date SDK that covers all possible AWS services.

STATUS
    Please consider the SDK is beta quality. The intention of publishing to CPAN is having the community find the SDK, try it, give
    feedback, etc. Some services are still not working, and some heavy refactoring will still be done to the internals. The external
    interface to SDK users will try to be kept stable, and changes to it should be notified via ChangeLog

SUPPORTED SERVICES

						

How do you access only the values of a Perl 5 hash?

With the values function


#player-grade
my %graduacoes = (
        'cegonha' 	=> 'azul',
        'creta' 	=> 'laranja-azul',
        'sol' 		=> 'amarela-laranja',
        'afogado' 	=> 'amarela',
        'lambarí' 	=> 'verde-roxa'
);

for my $corda (values %graduacoes) {
        say $corda;
}
						

laranja-azul
azul
verde-roxa
amarela
amarela-laranja
						

More questions?

Thank you and axé!