Este documento es una guía rápida del lenguaje de programación Perl 6. Para los novatos en Perl 6 sería un punto de partida y puesta en marcha.

Algunas partes de este documento hacen referencia a otras partes (más completas y precisas) de la Documentación de Perl 6. Consulta la documentación de Perl 6 si necesitas más información sobre algo concreto.

A lo largo de este documento encontrarás ejemplos de los temas más comentados. Es conveniente que pruebes todos los ejemplos para entenderlos bien.

Licencia

Este trabajo está bajo la licencia Creative Commons Attribution-ShareAlike 4.0 International License. Puedes encontrar una copia de esta licencia en

Colaboración

Puedes colaborar en este documento en:

Sugerencias y comentarios

Si te gusta este trabajo clica en Star del repositorio de Github.

1. Introducción

1.1. Qué es Perl 6

Perl 6 es un lenguaje de alto nivel, de propósito general y de tipado gradual. Perl 6 es multiparadigma y soporta programación Procedimental, Orientada a Objetos y Funcional.

El lema de Perl 6:
  • TMTOWTDI (Pronunciado como Tim Toady): There is more than one way to do it (Hay más de una forma para hacer algo).

  • Las cosas fáciles deben permanecer fáciles, las cosas difíciles deberían ser fáciles y las cosas imposibles deberían ser difíciles.

1.2. Jerga

  • Perl 6: Es una especificación con un banco de pruebas. Las implementaciones que pasan el banco de pruebas de la especificación se consideran Perl 6.

  • Rakudo: Es un compilador para Perl 6.

  • Rakudobrew: Es un script de Perl5 para instalar Rakudo desde su código fuente.

  • Panda: Es una herramienta para instalar módulos de Perl 6.

  • Rakudo Star: Es un paquete que incluye Rakudo, Panda, una colección de módulos de Perl 6 y documentación.

1.3. Instalación de Perl 6

Linux
  1. Instalación de Rakudobrew: https://github.com/tadzik/rakudobrew

  2. Instalación de Rakudo: En el terminal, introduce el siguiente comando rakudobrew build moar

  3. Instalación de Panda: En el terminal, introduce el siguiente comando rakudobrew build panda

OSX

Tienes cuatro opciones:

  • Sigue los mismos pasos de la instalación para Linux

  • Realizar la instalación con homebrew: brew install rakudo-star

  • Realizar la instalación con MacPorts: sudo port install rakudo

  • Descargar el último instalador (archivo con extensión .dmg) desde http://rakudo.org/download/star/

Windows
  1. Descarga el instalador más reciente (.msi) desde http://rakudo.org/downloads/star/
    Descarga la versión de 32-bit o 64-bit dependiendo de tu arquitectura.

  2. Finalizada la instalación, comprueba que C:\rakudo\bin figura en el PATH del sistema.

Docker
  1. Consigue la imagen oficial de Docker docker pull rakudo-star

  2. Y ejecuta un contenedor con la imagen docker run -it rakudo-star

1.4. Ejecutando código en Perl 6

Puedes ejecutar código Perl 6 mediante REPL (Read-Eval-Print Loop). Para ello, abre un terminal, introduce perl6 y pulsa [Enter]. Aparecerá el prompt >. Ahora introduce una línea de código, pulsa [Enter] y aparecerá una línea nueva con el resultado. Puedes introducir otra línea o exit y pulsar [Enter] para salir al sistema.

También puedes escribir tu código de Perl 6 en un archivo de texto, guardarlo y ejecutarlo. Es recomendable que el nombre de este archivo de texto tenga la extensión .pl6. Ejecuta el archivo de esta forma: perl6 nombre-archivo.pl6 desde un terminal y pulsa [Enter]. La ejecución suele mostrar el resultado de sentencias como say para visualizar por la salida estándar contenidos de texto con un salto de línea al final .

REPL normalmente se utiliza para probar trozos pequeños de código, como una línea. En el caso de programas con más de una línea de código es recomendable guardarlos en un archivo y ejecutarlos como hemos visto.

También puedes ejecutar una línea de código de forma "in-line" mediante el parámetro -e de la siguiente forma: perl6 -e 'línea de código Perl 6' y pulsando [Enter].

Rakudo Star incorpora un editor de líneas con más funcionalidades para REPL.

Si instalaste Rakudo en lugar de Rakudo Star es probable que no tengas estas funcionalidades (historial con flechas verticales, edición de la línea con flechas horizontales, autocompletar con TAB, etc.). Para instalar estas funcionalidades utiliza estos comandos:

  • panda install Linenoise debe funcionar en Windows, Linux y OSX

  • panda install Readline si tienes Linux y prefieres la librería Readline

1.5. Editores

Como casi siempre vamos a guardar nuestros programas de Perl 6 en archivos, necesitamos un editor de textos decente que reconozca la sintaxis de Perl 6.

Yo recomiendo y utilizo Atom. Es un editor de textos moderno que reconoce y visualiza bien la sintaxis de Perl 6. Perl6-fe es un paquete de Atom con una visualización alternativa de la sintaxis de Perl 6. Deriva del paquete original, tiene muchas correcciones y más funcionalidades.

En la comunidad también se utiliza Vim, Emacs o Padre.

Las últimas versiones de Vim incorporan la visualización de la sintaxis de Perl 6, mientras que Emacs y Padre necesitan instalar paquetes adicionales.

1.6. ¡Hola Mundo!

Comenzamos con El ritual hola mundo.

say 'hola mundo';

que también puede escribirse como:

'hola mundo'.say;

1.7. Sintaxis general

Perl 6 tiene forma libre, esto es, eres libre (casi siempre) de utilizar cualquier cantidad de espacios en blanco.

Una Sentencia normalmente es una línea lógica de código que finaliza en punto y coma: say "Hola" if True;

Las Expresiones son sentencias especiales que devuelven un valor: 1+2 devuelve 3

Las expresiones están formadas por Términos y Operadores.

Los Términos pueden ser:

  • Variables: Un valor que puede manipularse y ser cambiado.

  • Literales: Un valor constante como un número o un texto.

Los Operadores se clasifican en estos tipos:

Tipo

Significado

Ejemplo

Prefijo

Antes del término

++1

Infijo

Entre términos

1+2

Sufijo

Después del término

1++

Circumfijo

Al principio y al final del término

(1)

Precircumfijo

Después del término, al principio y al final de otro

Array[1]

1.7.1. Identificadores

Los identificadores son los nombres que se le dan a los términos cuando los defines.

Reglas:
  • Deben comenzar con un carácter alfabético o un guión bajo.

  • Pueden contener dígitos excepto en el primer carácter.

  • Pueden contener guión medio o apóstrofe seguido de un carácter alfabético, no al final.

Válido

No válido

var1

1var

var-uno

var-1

var’uno

var'1

var1_

var1'

_var

-var

Convención de nombres:
  • Notación Camello: variableNum1

  • Notación Kebab: variable-num1

  • Notación Serpiente: variable_num1

Puedes nombrar tus identificadores como quieras, pero es recomendable utilizar una convención consistente.

Utiliza nombres significativos para hacerlo más fácil, a tí y a los demás.

  • var1 = var2 * var3 es correcto pero no tiene un propósito evidente.

  • mes-salario = dia-frecuencia * dias-trabajo es una buena forma de nombrar las variables.

1.7.2. Comentarios

Un comentario es un texto, sirve como anotación y el compilador no lo tiene en cuenta.

Hay 3 tipos de comentarios:

  • De una línea:

    # Esto es un comentario de una línea
  • Incrustado:

    say #`(Esto es un comentario incrustado) "Hola Mundo."
  • De varias líneas:

    =begin comentario
    Esto es un comentario de varias líneas.
    Comentario 1
    Comentario 2
    =end comentario

1.7.3. Comillas

El texto tiene que ir entre comillas dobles o simples.

Utiliza siempre comillas dobles:

  • si el texto contiene un apóstrofe.

  • si el texto necesita visualizar el texto de una variable (interpolación de variable).

say 'Hola Mundo';   # Hola Mundo
say "Hola Mundo";   # Hola Mundo
say "Ven pa'ca cordera";    # Ven pa'ca cordera
my $nombre = 'Juan De Dios';
say 'Hola $nombre';   # Hola $nombre
say "Hola $nombre";   # Hola Juan De Dios

2. Operadores

2.1. Operadores comunes

La siguiente tabla muestra los operadores más utilizados.

Operador Tipo Descripción Ejemplo Resultado

+

Infijo

Suma

1 + 2

3

-

Infijo

Resta

3 - 1

2

*

Infijo

Multiplicación

3 * 2

6

**

Infijo

Potencia

3 ** 2

9

/

Infijo

División

3 / 2

1.5

div

Infijo

División Entera (redondeo inferior)

3 div 2

1

%

Infijo

Resto

7 % 4

3

%%

Infijo

Divisible

6 %% 4

Falso

6 %% 3

Verdadero

gcd

Infijo

Máximo común denominador

6 gcd 9

3

lcm

Infijo

Mínimo común múltiplo

6 lcm 9

18

==

Infijo

Igual numérico

9 == 7

Falso

!=

Infijo

No igual numérico

9 != 7

Verdadero

<

Infijo

Menor que

9 < 7

Falso

>

Infijo

Mayor que

9 > 7

Verdadero

<=

Infijo

Menor o igual

7 <= 7

Verdadero

>=

Infijo

Mayor o igual

9 >= 7

Verdadero

eq

Infijo

Texto igual

"Juan" eq "Juan"

Verdadero

ne

Infijo

Texto no igual

"Juan" ne "Juana"

Verdadero

=

Infijo

Asignación

my $var = 7

Asigna el valor 7 a la variable $var

~

Infijo

Texto concatenado

9 ~ 7

97

"Buenos " ~ "días"

Buenos días

x

Infijo

Texto replicado

13 x 3

131313

"Hola " x 3

Hola Hola Hola

~~

Infijo

Expresión regular

2 ~~ 2

Verdadero

2 ~~ Int

Verdadero

"Perl 6" ~~ "Perl 6"

Verdadero

"Perl 6" ~~ Str

Verdadero

"iluminación" ~~ /ilumina/

「ilumina」

++

Prefijo

Incremento

my $var = 2; ++$var;

Incrementa la variable por 1 y devuelve 3 como resultado

Sufijo

Incremento

my $var = 2; $var++;

Devuelve la variable 2 y después la incrementa

--

Prefijo

Decremento

my $var = 2; --$var;

Decrementa la variable en 1 y devuelve 1 como resultado

Sufijo

Decremento

my $var = 2; $var--;

Devuelve la variable 2 y después la decrementa

+

Prefijo

Fuerza el operando a un valor numérico

+"3"

3

+Verdadero

1

+Falso

0

-

Prefijo

Fuerza el operando a un valor numérico y devuelve la negación

-"3"

-3

-Verdadero

-1

-Falso

0

?

Prefijo

Fuerza el operando a un valor booleano

?0

Falso

?9.8

Verdadero

?"Hola"

Verdadero

?""

Falso

my $var; ?$var;

Falso

my $var = 7; ?$var;

Verdadero

!

Prefijo

Fuerza el operador a un valor booleano y devuelve la negación

!4

Falso

..

Infijo

Constructor de Series

0..5

Crea una serie de 0 a 5

..^

Infijo

Constructor de Series

0..^5

Crea una serie de 0 a 4

^..

Infijo

Constructor de Series

0^..5

Crea una serie de 1 a 5

^..^

Infijo

Constructor de Series

0^..^5

Crea una serie de 1 a 4

^

Prefijo

Constructor de Series

^5

Igual que 0..^5 Crea una serie de 0 a 4

…​

Infijo

Constructor de listas perezosas

0…​9999

devuelve los elementos si son solicitados

|

Prefijo

Aplanamiento

|(0..5)

(0 1 2 3 4 5)

|(0^..^5)

(1 2 3 4)

2.2. Intercambio de Operandos

Al agregar R delante de cualquier operador hace que se intercambien sus operandos.

Operación original Resultado Intercambio de operandos Resultado

2 / 3

0.666667

2 R/ 3

1.5

2 - 1

1

2 R- 1

-1

2.3. Reducción de Operadores

La reducción de operadores se utiliza en listas de valores. Se forman encerrando el operador entre corchetes []

Operación original Resultado Reducción de Operadores Resultado

1 + 2 + 3 + 4 + 5

15

[+] 1,2,3,4,5

15

1 * 2 * 3 * 4 * 5

120

[*] 1,2,3,4,5

120

En https://docs.perl6.org/language/operators tienes una lista completa de los operadores, incluyendo su precedencia.

3. Variables

Las variables en Perl 6 se reparten en tres categorías: Escalares, Arrays y Hashes.

Un sigilo (Signo en Latín) es un carácter utilizado como prefijo para categorizar variables.

  • $ para escalares

  • @ para arrays

  • % para hashes

3.1. Escalares

Un escalar contiene un valor o referencia.

#Texto
my $nombre = 'Juan De Dios';
say $nombre;

#Entero
my $edad = 99;
say $edad;

Dependiendo del valor contenido, un escalar puede realizar una serie de operaciones concretas.

Texto
my $nombre = 'Juan De Dios';
say $nombre.uc;
say $nombre.chars;
say $nombre.flip;
JUAN DE DIOS
12
soiD eD nauJ
Consulta https://docs.perl6.org/type/Str para ver una lista completa de métodos de texto.
Enteros
my $edad = 17;
say $edad.is-prime;
True
Consulta https://docs.perl6.org/type/Int para ver una lista completa de los métodos disponibles para enteros.
Números Racionales
my $edad = 2.3;
say $edad.numerator;
say $edad.denominator;
say $edad.nude;
23
10
(23 10)
Consulta https://docs.perl6.org/type/Rat para ver una lista completa de los métodos disponibles para números racionales.

3.2. Arrays

Los Arrays son listas que contienen varios valores.

my @animales = 'camello','llama','búho';
say @animales;

Los arrays permiten muchas operaciones, como las siguientes:

La tilde ~ se utiliza para concatenar texto.
Script
my @animales = 'camello','vicuña','llama';
say "El zoo tiene " ~ @animales.elems ~ " animales";
say "Los animales son: " ~ @animales;
say "He conseguido un búho para el zoo";
@animales.push("búho");
say "Los animales del zoo ahora son: " ~ @animales;
say "El primer animal del zoo es: " ~ @animales[0];
@animales.pop;
say "Desafortunadamente el búho se escapó y los animales que quedan son: " ~ @animales;
say "Vamos a dejar solo una animal en el zoo";
say "Dejamos ir a: " ~ @animales.splice(1,2) ~ " y dejamos en el zoo al " ~ @animales;
Salida
El zoo tiene 3 animales
Los animales son: camello vicuña llama
He conseguido un búho para el zoo
Los animales del zoo ahora son: camello vicuña llama búho
El primer animal del zoo es: camello
Desafortunadamente el búho se escapó y los animales que quedan son: camello vicuña llama
Vamos a dejar solo una animal en el zoo
Dejamos ir a: vicuña llama y dejamos en el zoo al camello
Explicación

.elems devuelve el número de elementos de un array.
.push() añade un elemento a un array.
Podemos acceder a un elemento concreto del array indicando su posición @animales[0].
.pop elimina el último elemento del array.
.splice(a,b) elimina b elementos que comienzan en la posición a.

3.2.1. Arrays de tamaño fijo

Un array básico se declara así:

my @array;

El array básico puede tener un número indefinido de valores y por eso se denomina auto-extendible.
Un array puede tener cualquier número de valores sin restricciones.

En contraste, también podemos crear arrays de tamaño fijo.
En estos arrays se define un tamaño fijo y no puede crecer más allá de este tamaño.

Para declarar un array de tamaño fijo, especifica el número máximo de elementos entre corchetes justo después de su nombre:

my @array[3];

Este array tendrá un máximo de 3 valores, indexados desde 0 a 2.

my @array[3];
@array[0] = "primer valor";
@array[1] = "segundo valor";
@array[2] = "tercer valor";

No puedes agregar un cuarto valor a este array:

my @array[3];
@array[0] = "primer valor";
@array[1] = "segundo valor";
@array[2] = "tercer valor";
@array[3] = "cuarto valor";
Index 3 for dimension 1 out of range (must be 0..2)

3.2.2. Arrays multidimensionales

Los arrays que hemos visto hasta ahora son de una dimensión.
Con Perl 6, podemos definir arrays de varias dimensiones.

my @tbl[3;2];

Este array es de dos dimensiones. La primera dimensión puede tener un máximo de 3 valores y la segunda dimensión un máximo de 2 valores.

Imagínalo como una tabla de 3x2.

my @tbl[3;2];
@tbl[0;0] = 1;
@tbl[0;1] = "x";
@tbl[1;0] = 2;
@tbl[1;1] = "y";
@tbl[2;0] = 3;
@tbl[2;1] = "z";
say @tbl
[[1 x] [2 y] [3 z]]
Representación visual del array:
[1 x]
[2 y]
[3 z]
Consulta https://docs.perl6.org/type/Array para tener la referencia completa sobre Arrays.

3.3. Hashes

Un Hash es una colección de pares Clave/Valor.
my %capitales = ('UK','Londres','Alemania','Berlín');
say %capitales;
Otra forma de insertar valores en un hash:
my %capitales = (UK => 'Londres', Alemania => 'Berlín');
say %capitales;

Algunos de los métodos aplicables a los hashes son:

Script
my %capitales = (UK => 'Londres', Alemania => 'Berlín');
%capitales.push: (Francia => 'París');
say %capitales.kv;
say %capitales.keys;
say %capitales.values;
say "La capital de Francia es: " ~ %capitales<Francia>;
Salida
(Alemania Berlín Francia París UK Londres)
(Alemania Francia UK)
(Berlín París Londres)
La capital de Francia es: París
Explicación

.push: (Clave ⇒ 'Valor') agrega un nuevo par clave/valor.
.kv devuelve una lista con todas las claves y valores.
.keys devuelve una lista con todas las claves.
.values devuelve una lista con todos los valores.
Podemos acceder a un valor concreto del hash indicando su clave %hash<clave>

Consulta https://docs.perl6.org/type/Hash para una referencia completa sobre hashes.

3.4. Tipos

En los ejemplos anteriores no hemos especificado el tipo de valor que debería contener cada variable.

.WHAT devuelve el tipo del valor que contiene la variable.
my $var = 'Texto';
say $var;
say $var.WHAT;

$var = 123;
say $var;
say $var.WHAT;

Como puedes ver en el ejemplo anterior, el tipo de valor en $var primero fue texto (Str) y después entero (Int).

Este estilo de programación se caracteriza por ser de tipado dinámico. Dinámico en el sentido de que las variables pueden contener valores de Cualquier tipo.

Ahora intenta ejecutar el siguiente ejemplo:
Fíjate en el Int indicado antes de la variable.

my Int $var = 'Texto';
say $var;
say $var.WHAT;

Este ejemplo devuelve un error indicando: Type check failed in assignment to $var; expected Int but got Str

Lo que ocurre es que hemos especificado como entero (Int) el tipo de la variable y falla al intentar asignar en ella un texto (Str).

Este estilo de programación se caracteriza por ser de tipado estático. Estático en el sentido de que la variable se define con un tipo determinado antes de asignarla y este tipo no puede cambiarse después.

Perl 6 es un lenguaje de tipado gradual; lo que permite tipado estático y dinámico.

Los arrays y hashes también pueden tener tipado estático:
my Int @array = 1,2,3;
say @array;
say @array.WHAT;

my Str @multilengua = "Hello","Salut","Hallo","您好","안녕하세요","こんにちは";
say @multilengua;
say @multilengua.WHAT;

my Str %capitales = (UK => 'London', Alemania => 'Berlín');
say %capitales;
say %capitales.WHAT;

my Int %código-país = (UK => 44, Alemania => 49);
say %código-país;
say %código-país.WHAT;
A continuación tienes una lista con los tipos más comunes:

Es posible que nunca utilices los dos primeros, pero aparecen en la siguiente lista para que sepas que existen.

Tipo

Descripción

Ejemplo

Resultado

Mu

La raíz de la jerarquía de tipos de Perl 6

Any

Clase base por defecto para nuevas clases y para la mayoría de las clases nativas

Cool

Valor que puede tratarse como texto o número indistintamente

my Cool $var = 31; say $var.flip; say $var * 2;

13 62

Str

Texto o cadena de carácteres

my Str $var = "NEON"; say $var.flip;

NOEN

Int

Entero (independientemente de la precisión)

7 + 7

14

Rat

Número racional (precisión limitada)

0.1 + 0.2

0.3

Bool

Booleano

!True

False

3.5. Introspección

Introspección es el proceso para adquirir información sobre las propiedades de un objeto, como por ejemplo su tipo.
En uno de los ejemplos anteriores utilizamos .WHAT para conocer el tipo de una variable.

my Int $var;
say $var.WHAT;    # (Int)
my $var2;
say $var2.WHAT;   # (Any)
$var2 = 1;
say $var2.WHAT;   # (Int)
$var2 = "Hola";
say $var2.WHAT;   # (Str)
$var2 = True;
say $var2.WHAT;   # (Bool)
$var2 = Nil;
say $var2.WHAT;   # (Any)

El tipo de una variable que contiene un valor se corresponde con su valor.
El tipo de una variable declarada de forma estática y sin valor es el tipo con el que se ha declarado.
El tipo de una variable vacía que no ha sido declarada de forma estática es (Any).
Asigna Nil a una variable para eliminar su valor.

3.6. Alcance

Es necesario declarar una variable antes de utilizarla.

Perl 6 dispone de varias formas de declaración, y en los siguientes ejemplos utilizaremos my.

my $var=1;

La forma de declaración my proporciona a la variable un alcance léxico. Dicho de otro modo, la variable solo es accesible desde el mismo bloque donde es declarada.

En Perl 6 un bloque está delimitado por { }.

En caso de no existir bloque, la variable estará disponible en el script entero.

{
  my Str $var = 'Texto';
  say $var; #accesible
}
say $var; #no accesible, da un error

Como la variable solo es accesible dentro del bloque donde está definida, la misma variable puede definirse de nuevo en cualquier otro bloque.

{
  my Str $var = 'Texto';
  say $var;
}
my Int $var = 123;
say $var;

3.7. Asignación vs. Vinculación

En los ejemplos anteriores hemos visto cómo asignar valores a variables.
La asignación se realiza mediante el operador =.

my Int $var = 123;
say $var;

Y podemos cambiar el valor asignado a la variable:

Asignación
my Int $var = 123;
say $var;
$var = 999;
say $var;
Salida
123
999

Por otro lado, no podemos cambiar el valor vinculado de una variable.

La vinculación se realiza mediante el operador :=.

Vinculación
my Int $var := 123;
say $var;
$var = 999;
say $var;
Salida
123
Cannot assign to an immutable value
Las variables también pueden vincularse a otras variables:
my $a;
my $b;
$b := $a;
$a = 7;
say $b;
$b = 8;
say $a;
Salida
7
8

Como has visto, la vinculación de variables es bidireccional.
$a := $b y $b := $a tienen el mismo efecto.

En https://docs.perl6.org/language/variables tienes más información sobre variables.

4. Funciones y mutadores

Es importante diferenciar entre funciones y mutadores.

Las funciones no cambian el estado inicial del objeto donde se aplica.

Los mutadores modifican el estado del objeto.

Script
1 2 3 4 5 6 7 8 9 10
my @números = [7,2,4,9,11,3]; @números.push(99); say @números; #1 say @números.sort; #2 say @números; #3 @números.=sort; say @números; #4
Salida
[7 2 4 9 11 3 99] #1
(2 3 4 7 9 11 99) #2
[7 2 4 9 11 3 99] #3
[2 3 4 7 9 11 99] #4
Explicación

.push es un mutador porque cambia el estado del array (#1)

.sort es una función porque devuelve un array ordenado pero no cambia el estado inicial del array:

  • (#2) muestra la devolución de un array ordenado.

  • (#3) muestra que el estado inicial del array no ha cambiado.

Puedes hacer que una función se comporte como un mutador utilizando .= en lugar de . (#4) (línea 9 del script)

5. Bucles y condiciones

Perl 6 tiene multitud de constructores de bucles y condiciones.

5.1. if

El código se ejecuta solo si se cumple la condición, o en otras palabras cuando la expresión se evalúa como True.

my $edad = 19;

if $edad > 18 {
  say 'Bienvenido'
}

En Perl 6 podemos invertir el código y la condición, y aún así la condición siempre se evalúa primero.

my $edad = 19;

say 'Bienvenido' if $edad > 18;

Si la condición no se cumple, podemos dar alternativas mediante bloques de ejecución utilizando:

  • else

  • elsif

#ejecuta el mismo código para distintos valores de la variable
my $número-de-asientos = 9;

if $número-de-asientos <= 5 {
  say 'Soy un sedan'
} elsif $número-de-asientos <= 7 {
  say 'Tengo 6 o 7 asientos'
} else {
  say 'Soy un microbus'
}

5.2. unless

La negación de if es unless.

El siguiente código:

my $limpiar-zapatos = False;

if not $limpiar-zapatos {
  say 'Limpia tus zapatos'
}

puede escribirse como:

my $limpiar-zapatos = False;

unless $limpiar-zapatos {
  say 'Limpia tus zapatos'
}

La negación en Perl 6 se realiza con ! o con not.

unless (condición) se utiliza en lugar de if not (condición).

unless no puede utilizar la claúsula else.

5.3. with

with es como if pero solo comprueba si la variable está definida.

my Int $var=1;

with $var {
  say 'Hola'
}

No ocurre nada si ejecutas el código sin asignar un valor a la variable.

my Int $var;

with $var {
  say 'Hola'
}

without es la negación de with y es parecido a unless.

Si la primera condición with no se cumple, puedes indicar una alternativa mediante orwith.
with y orwith son parecidos a if y elsif.

5.4. for

for itera sobre una serie de valores.

my @array = [1,2,3];

for @array -> $array-item {
  say $array-item * 100
}

Observa que en la iteración hemos creado la variable $array-item para realizar la operación *100 en cada elemento del array.

5.5. given

En Perl 6 given viene a ser switch en otros lenguajes.

my $var = 42;

given $var {
    when 0..50 { say 'Menos o igual a 50'}
    when Int { say "es un Entero" }
    when 42  { say 42 }
    default  { say "¿ejem?" }
}

Cuando se produce la coincidencia no se evalúan las demás.

Si utilizamos proceed continúa la evaluación aunque se produzca la coincidencia.

my $var = 42;

given $var {
    when 0..50 { say 'Menos o igual a 50';proceed}
    when Int { say "es un Entero";proceed}
    when 42  { say 42 }
    default  { say "¿ejem?" }
}

5.6. loop

loop es otra forma de escribir un for.

Actualmente loop viene a ser el for utilizado en la familia de lenguajes de C.

Perl 6 pertenece a la familia de lenguajes de C.

loop (my $i = 0; $i < 5; $i++) {
  say "El número actual es $i"
}
En https://docs.perl6.org/language/control tienes más información sobre bucles y condiciones

6. I/O

En Perl 6, las dos interfaces más utilizadas de Entrada/Salida son el Terminal y los Ficheros.

6.1. E/S básica mediante el Terminal

6.1.1. say

say escribe en la salida estándar agregando al final una línea nueva. En otras palabras, el siguiente código:

say 'Hola Mamá.';
say 'Hola Señor.';

escribirá dos líneas separadas.

6.1.2. print

Por otro lado print es como say pero no agrega la línea nueva.

Prueba a utilizar say en lugar de print y compara los resultados.

6.1.3. get

Para capturar la entrada desde el terminal utiliza get.

my $nombre;

say "¡Hola!, ¿cual es tu nombre?";
$nombre = get;

say "¿Que tal $nombre?, bienvenido a Perl 6";

Este código hace que el terminal espere la introducción de tu nombre y pulses [Enter] para después darte la bienvenida.

6.1.4. prompt

prompt es una combinación entre print y get.

El ejemplo anterior puede escribirse de esta otra forma:

my $nombre = prompt "¡Hola!, ¿cual es tu nombre? ";

say "¿Que tal $nombre?, bienvenido a Perl 6";

6.2. Ejecutando Comandos de la Shell

Podemos utilizar dos subrutinas para ejecutar comandos de la shell:

  • run Ejecuta un comando externo sin la intervención de la shell.

  • shell Ejecuta un comando desde la shell del sistema y dependerá de la plataforma y la shell. Todos los caracteres especiales los interpreta la shell, como pueden ser las tuberías, redirecciones, sustitución de variables de entorno, etc.

Ejecuta el siguiente script en Linux/OSX
my $nombre = 'Neo';
run 'echo', "Hola $nombre";
shell "ls";
Ejecuta lo siguiente en Windows
shell "dir";

echo y ls son palabras clave típicas de la shell de Linux:
echo visualiza texto en el terminal (es el equivalente a print en Perl 6)
ls muestra un listado de todos los archivos y carpetas del directorio actual

dir en Windows es el equivalente de ls en Linux.

6.3. E/S de Archivos

6.3.1. slurp

slurp lee datos de un archivo.

Crea un archivo de texto con el siguiente contenido:

datos.txt
Juan 9
Juanito 7
Juana 8
Juanita 7
my $datos = slurp "datos.txt";
say $datos;

6.3.2. spurt

spurt escribe datos en un archivo.

my $datos-nuevos = "Nuevas puntuaciones:
Pablo 10
Pablin 9
Paulo 11";

spurt "datos-nuevos.txt", $datos-nuevos;

El código anterior crea un nuevo archivo llamado datos-nuevos.txt conteniendo las nuevas puntuaciones.

6.4. Manipulando archivos y carpetas

En un ejemplo anterior ya hemos visto que Perl 6 puede mostrar el contenido de una carpeta (mediante ls) sin utilizar la shell.

say dir;               #Muestra archivos y carpetas de la carpeta actual
say dir "/Documentos"; #Muestra archivos y carpetas de la carpeta indicada

Además, puedes crear carpetas nuevas y eliminarlas.

mkdir "carpeta-nueva";
rmdir "carpeta-nueva";

mkdir crea una carpeta nueva.
rmdir elimina una carpeta vacía. Si la carpeta no existe devuelve un error.

También puedes comprobar si la ruta indicada existe y si se trata de un archivo o una carpeta:

Crea una carpeta vacía llamada carpeta123, un archivo vacío llamado script123.pl6 y el siguiente script:

say "script123.pl6".IO.e;
say "carpeta123".IO.e;

say "script123.pl6".IO.d;
say "carpeta123".IO.d;

say "script123.pl6".IO.f;
say "carpeta123".IO.f;

Ejecuta el script.

IO.e comprueba si existe la carpeta/archivo.
IO.f comprueba si la ruta es un archivo.
IO.d comprueba si la ruta es una carpeta.

en Windows puedes utilizar / o \\ para separar carpetas anidadas
C:\\rakudo\\bin
C:/rakudo/bin
En https://docs.perl6.org/type/IO tienes más información sobre E/S.

7. Subrutinas

7.1. Definición

Las Subrutinas (también denominadas subs o funciones) son una forma de empaquetar un conjunto de funcionalidades.

La definición de una subrutina comienza con la palabra clave sub. Una vez definida puede invocarse mediante su nombre.

Fíjate en el siguiente ejemplo:

sub saludo-alien {
  say "Hola terrícolas";
}

saludo-alien;

El ejemplo anterior es una subrutina sin entrada de datos.

7.2. Signatura

Muchas subrutinas utilizan argumentos de entrada para trabajar con ellos. El número y tipo de argumentos que acepta una subrutina se denomina su signatura.

La siguiente subrutina acepta un argumento de tipo string.

sub di-hola (Str $nombre) {
    say "¡¡Hola " ~ $nombre ~ "!!"
}
di-hola "Pablo";
di-hola "Paula";

7.3. Sobrecarga

Es posible definir varias subrutinas con el mismo nombre pero con distintas listas de parámetros o signaturas. Cuando se llama a la subrutina, se decidirá en tiempo de ejecución la versión de subrutina a utilizar dependiendo del número y tipo de argumentos proporcionados. Este tipo de subrutinas se definen con la palabra clave multi en lugar de sub.

multi saludo($nombre) {
    say "Buenos días $nombre";
}
multi saludo($nombre, $título) {
    say "Buenos días $título $nombre";
}

saludo "Juanito";
saludo "Laura","Srta.";

7.4. Argumentos por defecto y opcionales

Tendremos un error si se define una subrutina para aceptar un argumento y éste no es proporcionado.

Con Perl 6 podemos definir subrutinas con:

  • Argumentos opcionales

  • Argumentos por defecto

Un argumento opcional se define añadiendo ? al nombre del argumento.

sub di-hola($nombre?) {
  with $nombre { say "Hola " ~ $nombre }
  else { say "Hola humano" }
}
di-hola;
di-hola("Laura");

Si no se proporciona un argumento, puede definirse uno por defecto asignándole un valor en la definición de la subrutina.

sub di-hola($nombre="Mateo") {
  say "Hola " ~ $nombre;
}
di-hola;
di-hola("Laura");

7.5. Valores de retorno

Hemos visto que todas las subrutinas hasta ahora siempre hacen algo: mostrar resultados en la pantalla del terminal.

Esto es lo normal, pero a veces nos interesa que la subrutina devuelva algún tipo de valor que podamos utilizar después en el flujo del programa.

En condiciones normales la última línea de la subrutina se considera como el valor de retorno.

Retorno implícito
sub cuadrado ($x) {
  $x ** 2;
}
say "7 al cuadrado es igual a " ~ cuadrado(7);

Cuando hay mucho código, es mejor indicar de forma explícita qué es lo que queremos devolver. Esto se realiza mediante la palabra clave return.

sub cuadrado ($x) {
  return $x ** 2;
}
say "7 al cuadrado es igual a " ~ cuadrado(7);

7.5.1. Restricción de valores de retorno

En uno de los ejemplos anteriores vimos cómo restringir el tipo del argumento aceptado. Lo mismo podemos hacer con los valores de retorno.

Para restringir el valor de retorno a un tipo determinado podemos utilizar returns o la notación de flecha --> en la signatura.

Utilizando returns
sub cuadrado ($x) returns Int {
  return $x ** 2;
}
say "1.2 al cuadrado es igual a " ~ cuadrado(1.2);
Utilizando la notación de flecha
sub cuadrado ($x --> Int) {
  return $x ** 2;
}
say "1.2 al cuadrado es igual a " ~ cuadrado(1.2);

Si el tipo del valor devuelto no coincide con el indicado, tendremos un error.

Type check failed for return value; expected Int but got Rat (1.44)

La restricción del tipo del valor de retorno tambień puede controlar si este valor está definido o no.

En los ejemplos anteriores restringimos el valor de retorno a Int, esté definido o no. Además, podemos indicar que el valor de retorno esté obligatoriamente definido o no utilizando, de forma correspondiente, las siguientes signaturas:
-→ Int:D y -→ Int:U

Como hemos visto es una buena costumbre utilizar estas restricciones.
A continuación puedes ver la versión modificada del ejemplo anterior utilizando :D para que el tipo devuelto Int esté definido obligatoriamente.

sub cuadrado ($x --> Int:D) {
  return $x ** 2;
}
say "1.2 al cuadrado es igual a " ~ cuadrado(1.2);
En https://docs.perl6.org/language/functions encontrarás más información sobre subrutinas y funciones.

8. Programación Funcional

En este apartado veremos algunas funcionalidades relacionadas con la Programación Funcional.

8.1. Las Funciones son de primera clase

Las funciones/subrutinas son de primera clase:

  • Pueden utilizarse como un argumento

  • Pueden ser devueltas desde otra función

  • Pueden asignarse a una variable

Un buen ejemplo para demostrar este concepto es la función map.
map es una función de orden superior que acepta otra función como argumento.

Script
my @array = <1 2 3 4 5>;
sub cuadrado($x) {
  $x ** 2
}
say map(&cuadrado,@array);
Salida
(1 4 9 16 25)
Explicación

Hemos definido la subrutina cuadrado que calcula la potencia de dos de cualquier número proporcionado como argumento.
Después utilizamos map, una función de orden superior que toma dos argumentos: una subrutina y un array.
El resultado es una lista de todos los cuadrados de los elementos del array.

Ten en cuenta que cuando pasamos una subrutina como argumento, es necesario utilizar el prefijo & en el nombre.

8.2. Clausuras

Todos los objetos de código en Perl 6 son clausuras, lo que significa que se pueden referenciar variables léxicamente definidas desde un ámbito externo.

8.3. Funciones anónimas

Una función anónima también se denomina lambda.
Una función anónima no está vinculada a un identificador (no tiene nombre).

Escribamos de nuevo el ejemplo de map utilizando una función anónima

my @array = <1 2 3 4 5>;
say map(-> $x {$x ** 2},@array);

Observa que en lugar de declarar la función y pasarla a map como argumento, directamente la definimos dentro.
La función anónima -> $x {$x ** 2} no puede ser llamada.

En la jerga de Perl 6 a esta notación la llamamos punto de entrada al bloque

Un punto de entrada al bloque también puede utilizarse para asignar funciones a variables:
my $cuadrado = -> $x {
  $x ** 2
}
say $cuadrado(9);

8.4. Encadenamiento

En Perl 6 los métodos pueden encadenarse. Esto quiere decir que no es necesario pasar el resultado de un método como argumento de otro método.

¿Cómo obtendríamos los valores únicos de un array ordenados de mayor a menor?

Puedes resolver este problema escribiendo algo así:

my @array = <7 8 9 0 1 2 4 3 5 6 7 8 9>;
my @final-array = reverse(sort(unique(@array)));
say @final-array;

Primero utilizamos la función unique sobre @array, después pasamos el resultado como argumento a sort y por último pasamos el resultado de la ordenación a reverse.

En contraste con el ejemplo anterior, Perl 6 permite la encadenación de métodos.
El ejemplo anterior puede escribirse de la siguiente forma utilizando el encadenamiento de métodos:

my @array = <7 8 9 0 1 2 4 3 5 6 7 8 9>;
my @final-array = @array.unique.sort.reverse;
say @final-array;

Como ves, el encadenamiento de métodos es más visual.

8.5. Operador de Alimentación

El operador de alimentación, llamado tubería en algunos lenguajes de programación funcional hace aún más visual el encadenamiento de métodos.

Alimentación hacia adelante
my @array = <7 8 9 0 1 2 4 3 5 6 7 8 9>;
@array ==> unique()
       ==> sort()
       ==> reverse()
       ==> my @final-array;
say @final-array;
Explicación
Comienza con `@array` y devuelve una lista de elementos únicos
                    después los ordena
                    después invierte el orden
                    después guarda el resultado en @final-array

Como ves, el flujo de las llamadas a los métodos es de arriba hacia abajo.

Alimentación hacia atrás
my @array = <7 8 9 0 1 2 4 3 5 6 7 8 9>;
my @final-array-v2 <== reverse()
                   <== sort()
                   <== unique()
                   <== @array;
say @final-array-v2;
Explicación

La alimentación hacia atrás es parecida a la anterior pero se escribe en orden inverso.
El flujo de las llamadas a los métodos es de abajo hacia arriba.

8.6. Hiperoperador

El hiperoperador >>. puede aplicar un método a todos los elementos de una lista y devolver una lista con todos los resultados.

my @array = <0 1 2 3 4 5 6 7 8 9 10>;
sub es-par($var) { $var %% 2 };

say @array>>.is-prime;
say @array>>.&es-par;

Mediante el hiperoperador podemos utilizar todos los métodos ya definidos en Perl 6, por ej. is-prime que devuelve si un número es primo o no.
Además, podemos definir funciones nuevas y utilizarlas mediante el hiperoperador agregando el prefijo & en el nombre del método, por ej. &es-par.

El uso del hiperoperador es muy práctico pues evita escribir un bucle for para iterar sobre cada valor.

8.7. Ensamblajes

Un ensamblaje es una superposición lógica de valores.

En el siguiente ejemplo 1|2|3 es un ensamblaje.

my $var = 2;
if $var == 1|2|3 {
  say "La variable es 1 o 2 o 3"
}

El uso de ensamblajes normalmente produce autothreading para cada elemento del ensamblaje y todos los resultados se combinan y se devuelven en un nuevo ensamblaje.

8.8. Listas perezosas

Una lista perezosa es una lista que se evalúa perezosamente.
La evaluación perezosa demora la evaluación de una expresión hasta que es requerida, guardando mientras los resultados en una tabla de búsqueda para así evitar repetir la evaluación.

Entre los beneficios tenemos:

  • Incremento del rendimiento evitando cálculos innecesarios

  • La habilidad de construir estructuras de datos potencialmente infinitas

  • La habilidad de definir controles de flujo

Podemos definir una lista perezosa utilizando el operador infijo …​
Una lista perezosa tiene elemento(s) inicial(es), un generador y un punto final.

Lista perezonsa simple
my $listaperezosa = (1 ... 10);
say $listaperezosa;

El elemento inicial es 1 y el punto final es 10. Como no hemos definido un generador, por defecto es el sucesor (+1)
Dicho de otra forma, esta lista perezosa puede devolver (si es requerida) los siguientes elementos (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

Lista perezosa infinita
my $listaperezosa = (1 ... Inf);
say $listaperezosa;

Esta lista puede devolver (si es requerida) cualquier entero entre 1 e infinito, en otras palabras cualquier número entero.

Lista perezosa con generador deductivo
my $listaperezosa = (0,2 ... 10);
say $listaperezosa;

Los elementos iniciales son 0 y 2, y el punto final es 10. Aunque no hay un generador definido, Perl 6 utiliza los elementos iniciales para deducir que el generador es (+2)
Esta lista puede devolver (si es requerida) los siguientes elementos (0, 2, 4, 6, 8, 10)

Lista perezosa con generador definido
my $listaperezosa = (0, { $_ + 3 } ... 12);
say $listaperezosa;

En este ejemplo hemos definido de forma explícita un generador entre llaves { }
Esta lista puede devolver (si es requerida) los siguientes elementos (0, 3, 6, 9, 12)

Al usar un generador de forma explícita el punto final debe ser uno de los valores que el generador pueda devolver.
Si en el ejemplo anterior sustituimos el punto final 12 por un 10, el generador no se detendrá y saltará sobre el punto final y continuará.

De forma alternativa puedes sustituir 0 …​ 10 con 0 …​^ * > 10
Esto lo puedes leer como: De 0 hasta el primer valor mayor a 10 (excluyendo a éste)

Lo siguiente no detiene al generador
my $listaperezosa = (0, { $_ + 3 } ... 10);
say $listaperezosa;
Lo siguiente detiene al generador
my $listaperezosa = (0, { $_ + 3 } ...^ * > 10);
say $listaperezosa;

9. Clases y Objetos

En el apartado anterior hemos visto cómo utilizar la Programación Funcional en Perl 6 y en el siguiente apartado veremos cómo utilizar Perl 6 en la Programación Orientada a Objetos.

9.1. Introducción

La programación Orientada a Objetos es uno de los paradigmas de programación más utilizados actualmente.
Un objeto es un conjunto de variables y subrutinas.
Las variables se llaman atributos y las subrutinas se llaman métodos.
Los atributos definen un estado y los métodos definen el comportamiento de un objeto.

Una clase define la estructura de un conjunto de objetos.

Para entender esta relación veamos el siguiente ejemplo:

Hay 4 individuos en una sala

objetos ⇒ 4 personas

Los 4 individuos son humanos

clase ⇒ Humano

Tienen distintos nombres, edades, sexo y nacionalidad

atributos ⇒ nombre, edad, sexo, nacionalidad

En orientación a objetos decimos que los objetos son instancias de una clase.

Veamos el siguiente script:

class Humano {
  has $nombre;
  has $edad;
  has $sexo;
  has $nacionalidad;
}

my $juan = Humano.new(nombre => 'Juan', edad => 23, sexo => 'M', nacionalidad => 'Español');
say $juan;

La palabra clave class se utiliza para definir una clase.
La palabra clave has se utiliza para definir los atributos de una clase.
El método .new() se denomina constructor y crea el objeto como una instancia de la clase a la que ha sido llamada.

En el script anterior, la variable nueva $juan tiene una referencia a una instancia nueva de "Humano" definida por Humano.new().

Los argumentos que se pasan al método .new() son utilizados para establecer los atributos del objeto en cuestión.

Una clase puede tener un alcance léxico mediante my:

my class Humano {

}

9.2. Encapsulación

La encapsulación es un concepto de la programación Orientada a Objetos que consiste en empaquetar un conjunto de datos y métodos.

Los datos (atributos) dentro de un objeto deben ser privados, dicho de otro modo, solo son accesibles desde dentro del objeto.
Para acceder a los atributos de un objeto desde fuera de él utilizamos métodos de acceso.

Los siguientes dos scripts dan el mismo resultado.

Acceso directo a la variable:
my $var = 7;
say $var;
Encapsulación:
my $var = 7;
sub sayvar {
  $var;
}
say sayvar;

El método sayvar es un método de acceso que nos permite acceder al valor de la variable sin acceder directamente a ella.

Perl 6 realiza la encapsulación mediante twigils (sigilos secundarios) y se ubican entre el sigilo y el nombre del atributo.
En las clases se utilizan dos twigils:

  • ! para indicar de forma explícita que el atributo es privado.

  • . para crear automáticamente un método de accceso al atributo.

Por defecto todos los atributos son privados pero es muy recomendable utilizar siempre el twigil !.

Teniendo en cuenta lo dicho, podemos escribir de nuevo la clase anterior de la siguiente forma:

class Humano {
  has $!nombre;
  has $!edad;
  has $!sexo;
  has $!nacionalidad;
}

my $juan = Humano.new(nombre => 'Juan', edad => 23, sexo => 'M', nacionalidad => 'Español');
say $juan;

Si añades al script la siguiente sentencia: say $juan.edad;
devolverá el siguiente error: Method 'edad' not found for invocant of class 'Humano'
Esto es debido a que $!edad es un atributo privado y solo puede utilizarse desde dentro del objeto. Como hemos visto, tendremos un error al intentar acceder a este atributo desde fuera del objeto.

Sustituye has $!edad por has $.edad y comprueba el resultado de say $juan.edad;

9.3. Argumentos Posicionales vs. Argumentos con nombre

En Perl 6 todas las clases heredan un constructor .new() por defecto que puede utilizarse para crear objetos proporcionando argumentos.
El constructor por defecto solo acepta argumentos con nombre.
Como puedes ver en el ejemplo que vimos antes, los argumentos que tiene .new() están definidos con un nombre:

  • nombre ⇒ 'Juan'

  • edad ⇒ 23

¿Puedo ahorrarme el nombre de cada atributo al crear un objeto? Sí, pero necesito crear otro constructor que acepte argumentos posicionales.

class Humano {
  has $.nombre;
  has $.edad;
  has $.sexo;
  has $.nacionalidad;
  #nuevo constructor que sustituye el de por defecto.
  method new ($nombre,$edad,$sexo,$nacionalidad) {
    self.bless(:$nombre,:$edad,:$sexo,:$nacionalidad);
  }
}

my $juan = Humano.new('Juan',23,'M','Español');
say $juan;

9.4. Métodos

9.4.1. Introducción

Los métodos son las subrutinas de un objeto.
Al igual que las subrutinas, los métodos pueden empaquetar un conjunto de funcionalidades, aceptar argumentos, tener una signatura y estar sobrecargadas con multi.

Los métodos se definen con la palabra clave method y normalmente se utilizan para realizar alguna acción sobre los atributos de los objetos, reforzando así el concepto de encapsulación donde los atributos del objeto solo pueden manipularse dentro del objeto mediante sus métodos. Desde fuera solo podemos acceder a los métodos de los objetos y no a sus atributos.

class Humano {
  has $.nombre;
  has $.edad;
  has $.sexo;
  has $.nacionalidad;
  has $.es-adulto;
  method evalúa_es-adulto {
      if self.edad < 18 {
        $!es-adulto = 'No'
      } else {
        $!es-adulto = 'Sí'
      }
  }
}

my $juan = Humano.new(nombre => 'Juan', edad => 23, sexo => 'M', nacionalidad => 'Español');
$juan.evalúa_es-adulto;
say $juan.es-adulto;

Una vez definidos los métodos de una clase, pueden invocarse en un objeto mediante la notación de punto:
objeto . método, como en el ejemplo que hemos visto antes: $juan.evalúa_es-adulto

Si en la definición del método necesitamos hacer referencia al objeto en sí para invocar a otro método utilizaremos la palabra clave self.
Si en la definición del método necesitamos hacer referencia a un atributo utilizaremos ! aunque el atributo esté definido con .
Mientras el twigil . declara el atributo, con ! se realiza el método de acceso. En el ejemplo anterior if self.edad < 18 y if $!edad < 18 tendrán el mismo efecto, aunque técnicamente son distintos:

  • self.edad es una llamada al método (de acceso) .edad
    También puede escribirse como $.edad

  • $!edad es una llamada directa a la variable

9.4.2. Métodos privados

Puede llamarse a un método normal de un objeto desde fuera de la clase.

Los métodos privados solo pueden llamarse desde dentro de la clase.
Este es el caso donde un método llama a otro para realizar una acción concreta. El método que interactúa con el mundo exterior es público y a la vez llama al otro método que permanece privado. Al declarar el método como privado conseguimos que el usuario no pueda interactuar con él directamente.

Declarar un método privado requiere utilizar el twigil ! antes de su nombre.
Estos métodos privados se llaman mediante ! en lugar de .

method !soyprivado {
  #código
}

method soypúblico {
  self!soyprivado;
  #más código
}

9.5. Atributos de Clase

Los atributos de Clase son atributos que pertenecen a la clase en sí y no a sus objetos.
Pueden inicializarse durante su definición.
Los atributos de Clase se declaran mediante my en lugar de has.
Se llaman en la clase en sí en lugar de sus objetos.

class Humano {
  has $.nombre;
  my $.contador = 0;
  method new($nombre) {
    Humano.contador++;
    self.bless(:$nombre);
  }
}
my $a = Humano.new('a');
my $b = Humano.new('b');

say Humano.contador;

9.6. Tipo de Acceso

Todos los ejemplos que hemos visto hasta ahora utilizan métodos de acceso para acceder a la información de los atributos de los objetos.

¿Y si necesitamos modificar el valor de un atributo?
Para ello necesitamos etiquetar ese atributo como lectura/escritura mediante la palabra clave is rw

class Humano {
  has $.nombre;
  has $.edad is rw;
}
my $juan = Humano.new(nombre => 'Juan', edad => 21);
say $juan.edad;

$juan.edad = 23;
say $juan.edad;

Todos los atributos se declaran por defecto como solo lectura y también puedes hacerlo de forma explícita mediante is readonly

9.7. Herencia

9.7.1. Introducción

Herencia es otro concepto de la programación Orientada a Objetos.

Cuando definimos clases nos damos cuenta de que algunas veces utilizan los mismos métodos y atributos.
¿Es necesario duplicar código?
¡NO! Hay que utilizar la herencia

Pensemos en definir dos clases, una clase para seres humanos y otra clase para empleados.
Los seres humanos tienen 2 atributos: nombre y edad.
Los empleados tienen 4 atributos: nombre, edad, compañía y salario.

Con prisas, uno definiría las clases de la siguiente forma:

class Humano {
  has $.nombre;
  has $.edad;
}

class Empleado {
  has $.nombre;
  has $.edad;
  has $.compañía;
  has $.salario;
}

El código anterior aunque técnicamente es correcto, conceptualmente es pobre.

Hay una forma mejor de escribirlo:

class Humano {
  has $.nombre;
  has $.edad;
}

class Empleado is Humano {
  has $.compañía;
  has $.salario;
}

La herencia se define mediante la palabra clave is.
En orientación a objetos decimos que Empleado es hijo de Humano, y Humano es padre de Empleado.

Todas las clases hijas heredan los atributos y métodos de su clase padre, y así ahorramos duplicar su definición.

9.7.2. Anulación de herencia

Las clases heredan todos los atributos y métodos de sus clases padre correspondientes.
Hay casos donde es necesario que un método heredado actúe de forma distinta.
Para conseguirlo, redefinimos el método en cuestión en la clase hija.
Este concepto se llama anulación de herencia.

En el siguiente ejemplo, el método preséntate se hereda de la clase Empleado.

class Humano {
  has $.nombre;
  has $.edad;
  method preséntate {
    say 'Hola, soy un ser humano y mi nombre es ' ~ self.nombre;
  }
}

class Empleado is Humano {
  has $.compañía;
  has $.salario;
}

my $juan = Humano.new(nombre =>'Juan', edad => 23,);
my $juana = Empleado.new(nombre =>'Juana', edad => 25, compañía => 'Acme', salario => 4000);

$juan.preséntate;
$juana.preséntate;

La anulación de herencia funciona así:

class Humano {
  has $.nombre;
  has $.edad;
  method preséntate {
    say 'Hola, soy un ser humano y mi nombre es ' ~ self.nombre;
  }
}

class Empleado is Humano {
  has $.compañía;
  has $.salario;
  method preséntate {
    say 'Hola, soy un empleado, mi nombre es ' ~ self.nombre ~ ' y trabajo en: ' ~ self.compañía;
  }
}

my $juan = Humano.new(nombre =>'Juan',edad => 23,);
my $juana = Empleado.new(nombre =>'Juana',edad => 25,compañía => 'Acme',salario => 4000);

$juan.preséntate;
$juana.preséntate;

El método correspondiente será aplicado dependiendo de la clase a la que pertenece el objeto.

9.7.3. Submétodos

Los submétodos son métodos que no se heredan en las clases hijas.
Solo son accesibles desde la clase donde son declarados.
Se definen utilizando la palabra clave submethod.

9.8. Herencia Múltiple

Perl 6 permite la herencia múltiple. Una clase puede heredar de varias clases.

class graf-barras {
  has Int @.valores-barras;
  method dibujar {
    say @.valores-barras;
  }
}

class graf-líneas {
  has Int @.valores-líneas;
  method dibujar {
    say @.valores-líneas;
  }
}

class multi-gráfica is graf-barras is graf-líneas {
}

my $ventas-actuales = graf-barras.new(valores-barras => [10,9,11,8,7,10]);
my $previsión-ventas = graf-líneas.new(valores-líneas => [9,8,10,7,6,9]);

my $actual-vs-previsión = multi-gráfica.new(valores-barras => [10,9,11,8,7,10],
                                            valores-líneas => [9,8,10,7,6,9]);
say "Ventas actuales:";
$ventas-actuales.dibujar;
say "Previsión de ventas:";
$previsión-ventas.dibujar;
say "Actual vs Previsión:";
$actual-vs-previsión.dibujar;
Salida
Ventas actuales:
[10 9 11 8 7 10]
Previsión de ventas:
[9 8 10 7 6 9]
Actual vs Previsión:
[10 9 11 8 7 10]
Explicación

La clase multi-gráfica debería ser capaz de tener dos series, una para los valores actuales de las barras y otra para los valores de las previsiones de las líneas.
Por esa razón la hemos definido como hija de graf-líneas y graf-barras.
Te habrás dado cuenta que al llamar al método dibujar en multi-gráfica no tenemos el resultado deseado. Solo se dibuja una serie.
¿Qué ha ocurrido?
multi-gráfica hereda de graf-líneas y de graf-barras y ambas tienen un método llamado dibujar. Cuando llamamos a ese método desde multi-gráfica Perl 6 trata de resolver internamente el conflicto llamando a uno de los métodos heredados.

Correción

Para que funcione correctamente necesitamos anular la herencia del método dibujar en multi-gráfica.

class graf-barras {
  has Int @.valores-barras;
  method dibujar {
    say @.valores-barras;
  }
}

class graf-líneas {
  has Int @.valores-líneas;
  method dibujar {
    say @.valores-líneas;
  }
}

class multi-gráfica is graf-barras is graf-líneas {
  method dibujar {
    say @.valores-barras;
    say @.valores-líneas;
  }
}

my $ventas-actuales = graf-barras.new(valores-barras => [10,9,11,8,7,10]);
my $previsión-ventas = graf-líneas.new(valores-líneas => [9,8,10,7,6,9]);

my $actual-vs-previsión = multi-gráfica.new(valores-barras => [10,9,11,8,7,10],
                                            valores-líneas => [9,8,10,7,6,9]);
say "Ventas actuales:";
$ventas-actuales.dibujar;
say "Previsión de ventas:";
$previsión-ventas.dibujar;
say "Actual vs Previsión:";
$actual-vs-previsión.dibujar;
Salida
Ventas actuales:
[10 9 11 8 7 10]
Previsión de ventas:
[9 8 10 7 6 9]
Actual vs Previsión:
[10 9 11 8 7 10]
[9 8 10 7 6 9]

9.9. Roles

Los Roles se parecen a las clases en cuanto a que son una colección de atributos y métodos.

Los roles se declaran con la palabra clave role y las clases que quieran implementar el rol pueden hacerlo mediante la palabra clave does.

Vamos a escribir de nuevo el ejemplo de la herencia múltiple pero mediante roles:
role graf-barras {
  has Int @.valores-barras;
  method dibujar {
    say @.valores-barras;
  }
}

role graf-líneas {
  has Int @.valores-líneas;
  method dibujar {
    say @.valores-líneas;
  }
}

class multi-gráfica does graf-barras does graf-líneas {
  method dibujar {
    say @.valores-barras;
    say @.valores-líneas;
  }
}

my $ventas-actuales = graf-barras.new(valores-barras => [10,9,11,8,7,10]);
my $previsión-ventas = graf-líneas.new(valores-líneas => [9,8,10,7,6,9]);

my $actual-vs-previsión = multi-gráfica.new(valores-barras => [10,9,11,8,7,10],
                                            valores-líneas => [9,8,10,7,6,9]);
say "Ventas actuales:";
$ventas-actuales.dibujar;
say "Previsión de ventas:";
$previsión-ventas.dibujar;
say "Actual vs Previsión:";
$actual-vs-previsión.dibujar;

Verás que el resultado es el mismo que antes sin utilizar roles.

Y ahora te preguntarás; si un rol es como una clase ¿cual es la función del rol?
Para responder la pregunta, modifica el primer script que hemos utilizado para mostrar el caso de la herencia múltiple, en el que olvidamos anular la herencia del método dibujar, pero utilizando roles nuevamente.

role graf-barras {
  has Int @.valores-barras;
  method dibujar {
    say @.valores-barras;
  }
}

role graf-líneas {
  has Int @.valores-líneas;
  method dibujar {
    say @.valores-líneas;
  }
}

class multi-gráfica does graf-barras does graf-líneas {
}

my $ventas-actuales = graf-barras.new(valores-barras => [10,9,11,8,7,10]);
my $previsión-ventas = graf-líneas.new(valores-líneas => [9,8,10,7,6,9]);

my $actual-vs-previsión = multi-gráfica.new(valores-barras => [10,9,11,8,7,10],
                                            valores-líneas => [9,8,10,7,6,9]);
say "Ventas actuales:";
$ventas-actuales.dibujar;
say "Previsión de ventas:";
$previsión-ventas.dibujar;
say "Actual vs Previsión:";
$actual-vs-previsión.dibujar;
Salida
===SORRY!=== Error while compiling
Method 'dibujar' must be resolved by class multi-gráfica because it exists in multiple roles (graf-líneas, graf-barras)
Explicación

Tendremos un error en tiempo de compilación si aplicamos varios roles a la misma clase mientras exista un conflicto.
Este enfoque es mucho más seguro que la herencia múltiple donde los conflictos no se consideran errores y se resuelven simplemente en tiempo de ejecución.

Los roles te avisarán si existe un conflicto.

9.10. Introspección

La Introspección es la forma de ver las propiedades de un objeto; como el tipo, atributos o métodos.

class Humano {
  has Str $.nombre;
  has Int $.edad;
  method preséntate {
    say 'Hola, soy un ser humano y mi nombre es ' ~ self.nombre;
  }
}

class Empleado is Humano {
  has Str $.compañía;
  has Int $.salario;
  method preséntate {
    say 'Hola, soy un empleado, mi nombre es ' ~ self.nombre ~ ' y trabajo en: ' ~ self.compañía;
  }
}

my $juan = Humano.new(nombre =>'Juan',edad => 23,);
my $juana = Empleado.new(nombre =>'Juana',edad => 25,compañía => 'Acme',salario => 4000);

say $juan.WHAT;
say $juana.WHAT;
say $juan.^attributes;
say $juana.^attributes;
say $juan.^methods;
say $juana.^methods;
say $juana.^parents;
if $juana ~~ Humano {say 'Juana es Humana'};

La introspeción proporciona la siguiente información:

  • .WHAT devuelve la clase a la que pertenece el objeto.

  • .^attributes devuelve una lista con todos los atributos del objeto.

  • .^methods devuelve todos los métodos accesibles del objeto.

  • .^parents devuelve todas las clases padre a las que pertenece la clase del objeto.

  • ~~ es el operador de coincidencia inteligente. Devuelve True si el objeto pertenece a la clase con la que se compara o con cualquier clase heredada.

Consulta:

para obtener más información sobre Programación Orientada a Objetos en Perl6.

10. Control de Excepciones

10.1. Captura de Excepciones

Las excepciones son situaciones especiales que ocurren en tiempo de ejecución cuando algo va mal.
Decimos que las excepciones son lanzadas.

Veamos una ejecución correcta como en el siguiente script:

my Str $nombre;
$nombre = "Juana";
say "Hola " ~ $nombre;
say "¿Qué haces hoy?"
Salida
Hola Juana
¿Qué haces hoy?

Ahora veamos un script que lanza una excepción:

my Str $nombre;
$nombre = 123;
say "Hola " ~ $nombre;
say "¿Qué haces hoy?"
Salida
Type check failed in assignment to $nombre; expected Str but got Int
  in block <unit> at exceptions.pl6 line 2

Debes tener en cuenta que cuando se produce un error (en este caso debido a la asignación de un número a una variable de texto) el programa se interrumpirá y no ejecutará las siguientes líneas de código aunque éstas sean correctas.

El Control de excepciones se produce cuando se lanza una excepción y es capturada de forma que el script continúa su ejecución.

my Str $nombre;
try {
  $nombre = 123;
  say "Hola " ~ $nombre;
  CATCH {
    default {
      say "¿Puedes decirme tu nombre de nuevo? No podemos encontrarlo en el registro.";
    }
  }
}
say "¿Qué haces hoy?";
Salida
¿Puedes decirme tu nombre de nuevo? No podemos encontrarlo en el registro.
¿Qué haces hoy?

El Control de excepciones se realiza utilizando un bloque try-catch.

try {
  #código
  #si algo va mal, el script saltará al bloque CATCH
  #si todo es correcto, el script ignorará el bloque CATCH
  CATCH {
    default {
      #aquí se ejecuta código si se lanza una excepción
    }
  }
}

El bloque CATCH puede definirse igual que el bloque given. Esto significa que podemos capturar y controlar distintos tipos de excepciones.

try {
  #código
  #si algo va mal, el script saltará al bloque CATCH
  #si todo es correcto, el script ignorará el bloque CATCH
  CATCH {
    when X::AdHoc { #hace algo si se lanza una excepción de tipo X::AdHoc }
    when X::IO { #hace algo si se lanza una excepción de tipo X::IO }
    when X::OS { #hace algo si se lanza una excepción de tipo X::OS }
    default { #hace algo si se lanza una excepción y no está contemplada en los tipos anteriores }
  }
}

10.2. Lanzando Excepciones

Además de capturar excepciones, Perl 6 te permite lanzar excepciones de forma explícita.
Se pueden lanzar dos tipos de excepciones:

  • Excepciones ad-hoc

  • Excepciones por tipo

ad-hoc
my Int $edad = 21;
die "¡Error!";
por tipo
my Int $edad = 21;
X::AdHoc.new(payload => '¡Error!').throw;

Las excepciones ad-hoc se lanzan utilizando la subrutina die seguida del mensaje describiendo la excepción.

Las excepciones por tipo son objetos, y como vemos en el ejemplo anterior utilizan el constructor .new().
Todas las excepciones por tipo pertenecen a la clase X. Estos son algunos ejemplos:
X::AdHoc es el tipo de excepción más simple
X::IO errores relacionados con operaciones de E/S
X::OS errores relacionados con el Sistema Operativo
X::Str::Numeric errores relacionados con la conversión de una cadena de texto a un valor numérico

Tienes una lista completa de tipos de excepciones y sus métodos asociados en https://docs.perl6.org/type-exceptions.html

11. Expresiones Regulares

Una expresión regular, o regex es una secuencia de caracteres que sirve para encontrar un patrón.
Para entenderlo mejor piensa en ello como un patrón.

if 'iluminación' ~~ m/ ilumina / {
    say "iluminación contiene la palabra ilumina";
}

En este ejemplo, el operador inteligente de coincidencia ~~ sirve para comprobar si el texto (iluminación) contiene la palabra (ilumina).
"Iluminación" se compara con la regex m/ ilumina /

11.1. Definición de Regex

Una expresión regular puede definirse de las siguientes formas:

  • /ilumina/

  • m/ilumina/

  • rx/ilumina/

A menos que se indique de forma explícita, el espacio en blanco es ignorado, da igual m/ilumina/ que m/ ilumina /.

11.2. Búsqueda de caracteres

Los caracteres alfanuméricos y el guión bajo _ se escriben tal cual.
El resto de caracteres deben ser escapados utilizando la barra invertida o backslash o ir entre comillas.

Barra invertida o Backslash
if 'Temperatura: 13' ~~ m/ \: / {
    say "El texto contiene el caracter dos puntos :";
}
Comillas simples
if 'Edad = 13' ~~ m/ '=' / {
    say "El texto contiene el caracter igual = ";
}
Comillas dobles
if 'nombre@empresa.com' ~~ m/ "@" / {
    say "Dirección de mail válida porque contiene el caracter @";
}

11.3. Categorías de caracteres

Los caracteres se pueden clasificar en categorías y podemos realizar comparaciones con ellas.
También podemos comparar la inversa de la categoría (todo menos ella):

Categoría

Regex

Inversa

Regex

Caracter de palabra (letra, dígito o guión bajo)

\w

Cualquier caracter menos un caracter de palabra

\W

Dígito

\d

Cualquier caracter menos un dígito

\D

Espacio en blanco

\s

Cualquier caracter menos un espacio en blanco

\S

Espacio en blanco horizontal

\h

Cualquier caracter menos un caracter en blanco horizontal

\H

Espacio en blanco vertical

\v

Cualquier caracter menos un caracter en blanco horizontal

\V

Tabulador

\t

Cualquier caracter menos el tabulador

\T

Línea nueva

\n

Cualquier caracter menos una línea nueva

\N

if "Juan123" ~~ / \d / {
  say "Nombre no válido, no se permiten números";
} else {
  say "Nombre válido"
}
if "Juan-Dios" ~~ / \s / {
  say "El texto contiene un espacio en blanco";
} else {
  say "El texto no contiene un espacio en blanco"
}

11.4. Propiedades Unicode

Lo normal es comparar categorías de caracteres como hemos visto.
Dicho esto, podemos tener un enfoque más sistemático utilizando propiedades Unicode.
Las propiedades Unicode se indican entre <: >

if "Juan123" ~~ / <:N> / {
  say "Contiene un número";
} else {
  say "No contiene un número"
}
if "Juan-Dios" ~~ / <:Lu> / {
  say "Contiene una letra en mayúsculas";
} else {
  say "No contiene una letra en mayúsculas"
}
if "Juan-Dios" ~~ / <:Pd> / {
  say "Contiene un guión";
} else {
  say "No contiene un guión"
}

11.5. Comodines

En una regex también se pueden utilizar comodines.

El punto . significa cualquier caracter.

if 'abc' ~~ m/ a.c / {
    say "Coincide";
}
if 'a2c' ~~ m/ a.c / {
    say "Coincide";
}
if 'ac' ~~ m/ a.c / {
    say "Coincide";
  } else {
    say "No coincide";
}

11.6. Cuantificadores

Los cuantificadores van después de un caracter y especifican cuantas veces se repite éste.

El interrogante ? significa que se repite una vez o ninguna.

if 'ac' ~~ m/ a?c / {
    say "Coincide";
  } else {
    say "No coincide";
}
if 'c' ~~ m/ a?c / {
    say "Coincide";
  } else {
    say "No coincide";
}

El asterisco * significa que se repite una vez o más de una vez o ninguna.

if 'az' ~~ m/ a*z / {
    say "Coincide";
  } else {
    say "No coincide";
if 'aaz' ~~ m/ a*z / {
    say "Coincide";
  } else {
    say "No coincide";
}
if 'aaaaaaaaaaz' ~~ m/ a*z / {
    say "Coincide";
  } else {
    say "No coincide";
}
if 'z' ~~ m/ a*z / {
    say "Coincide";
  } else {
    say "No coincide";
}

El símbolo + significa que se repite al menos una vez.

if 'az' ~~ m/ a+z / {
    say "Coincide";
  } else {
    say "No coincide";
}
if 'aaz' ~~ m/ a+z / {
    say "Coincide";
  } else {
    say "No coincide";
}
if 'aaaaaaaaaaz' ~~ m/ a+z / {
    say "Coincide";
  } else {
    say "No coincide";
}
if 'z' ~~ m/ a+z / {
    say "Coincide";
  } else {
    say "No coincide";
}

11.7. Extracción de resultados

Cuando se encuentra el patrón buscado, el resultado se guarda en la variable especial $/

Script
if 'Rakudo es el compilador de Perl 6' ~~ m/:s Perl 6/ {
    say "El resultado es: " ~ $/;
    say "El texto antes del resultado es: " ~ $/.prematch;
    say "El texto después del resultado es: " ~ $/.postmatch;
    say "La posición de comienzo del resultado es: " ~ $/.from;
    say "La posición final del resultado es: " ~ $/.to;
}
Salida
El resultado es: Perl 6
El texto antes del resultado es: Rakudo es el compilador de
El texto después del resultado es:
La posición inicial del resultado es: 27
La posición final del resultado es: 33
Explicación

$/ devuelve un Objeto de Coincidencia (el texto encontrado o resultado de la regex)
El Objeto de Coincidencia tiene los siguientes métodos:
.prematch devuelve el texto que hay antes del resultado.
.postmatch devuelve el texto que hay después del resultado.
.from devuelve la posición inicial del resultado.
.to devuelve la posición final del resultado.

Por defecto el espacio en blanco en una regex es irrelevante.
Si queremos tener en cuenta los espacios en blanco en una regex tenemos que hacerlo de forma explícita.
El parámetro :s en la regex m/:s Perl 6/ hace que la regex tenga en cuenta los espacios en blanco.
Otra forma de hacerlo sería así m/ Perl\s6 / donde \s, como hemos visto antes, es la regex para encontrar el espacio en blanco.
Si la regex contiene más de un espacio en blanco, es más efectivo utilizar :s que utilizar \s para cada espacio en blanco.

11.8. Ejemplo

Vamos a comprobar si una dirección de email es correcta o no.
Para este ejemplo asumiremos que una dirección de email correcta está formada así:
nombre [punto] apellido [arroba] compañía [punto] (com/org/net)

La regex que utilizaremos en este ejemplo para validar una dirección de email no es muy precisa, y como su propósito es demostrar el funcionamiento de las regex en Perl 6, conviene no utilizarla en producción.
Script
my $email = 'juan.dios@perl6.org';
my $regex = / <:L>+\.<:L>+\@<:L+:N>+\.<:L>+ /;

if $email ~~ $regex {
  say $/ ~ " es un email válido";
} else {
  say "No es una email válido";
}
Salida

juan.dios@perl6.org es un email válido

Explicación

<:L> coincide con una letra
<:L>+ coincide con una letra o más de una
\. coincide con un caracter de [punto]
\@ coincide con un caracter de [arroba]
<:L+:N> coincide con una letra o más de una y un número
<:L+:N>+ coincide con una o más (una o más letras y un número)

La regex se puede descomponer así:

  • nombre <:L>+

  • [punto] \.

  • apellido <:L>+

  • [arroba] \@

  • nombre de la compañía <:L+:N>+

  • [punto] \.

  • com/org/net <:L>+

Otra forma de hacerlo es descomponer la regex en varias regex con nombre
my $email = 'juan.dios@perl6.org';
my regex varias-letras { <:L>+ };
my regex punto { \. };
my regex arroba { \@ };
my regex varias-letras-numeros { <:L+:N>+ };

if $email ~~ / <varias-letras> <punto> <varias-letras> <arroba> <varias-letras-numeros> <punto> <varias-letras> / {
  say $/ ~ " es un email válido";
} else {
  say "No es una email válido";
}

Una regex con nombre se define de la siguiente forma: my regex nombre-regex { definición de la regex }
Para utilizar una regex, la invocamos con su nombre de esta forma: <nombre-regex>

En https://docs.perl6.org/language/regexes tienes más información sobre regex.

12. Módulos en Perl 6

Perl 6 es un lenguaje de programación de propósito general. Puede utilizarse para llevar a cabo multitud de tareas, incluyendo: manipulación de texto, gráficos, web, bases de datos, protocolos de red, etc.

La reutilización es un concepto muy importante para que los programadores no tengan que reinventar la rueda cada vez que quieran llevar a cabo una nueva tarea.

Perl 6 permite la creación y redistribución de módulos. Cada módulo es un paquete de funcionalidades que, una vez instalado, se puede reutilizar.

Panda es el gestor de módulos que incorpora Rakudo.

Para instalar un módulo concreto, utiliza el siguiente comando en el terminal:

panda install "nombre del módulo"

Puedes encontrar el directorio de módulos de Perl 6 en: https://modules.perl6.org/

12.1. Utilizando Módulos

MD5 es una función de cifrado de tipo hash que produce un valor hash de 128-bit.
MD5 tiene muchas aplicaciones, como el cifrado de contraseñas que se pueden alojar en una base de datos. Cuando se registra un nuevo usuario sus credenciales no se guardan en texto plano, se guardan cifradas, de forma que si un atacante compromete la base de datos, este atacante no podría conocer las contraseñas. Supongamos que necesitas un script que genere el hash MD5 de una contraseña para después guardarlo en la base de datos.

Por suerte existe un módulo en Perl 6 que ya implementa el algoritmo MD5. Vamos a instalarlo:
panda install Digest::MD5

Ahora ejecutemos el siguiente script:

use Digest::MD5;
my $contraseña = "contraseña123";
my $contraseña-cifrada = Digest::MD5.new.md5_hex($contraseña);

say $contraseña-cifrada;

Para utilizar la función md5_hex() que produce el cifrado necesitaremos antes cargar el módulo requerido.
La palabra clave use carga el módulo para después utilizarlo en el script.

En la práctica el cifrado MD5 no es suficientemente seguro pues es vulnerable a ataques de diccionario.
Debería combinarse con sal https://es.wikipedia.org/wiki/Sal_(criptografía).

13. Unicode

Unicode es un estándar para codificar y representar texto que incluye la mayoría de los sistemas de escritura del mundo.
UTF-8 es una codificación de caracteres que puede codificar todos los caracteres o números de código en Unicode.

Los caracteres se definen con un:
Grafema: Representación visual.
Número de código: Un número asignado a un caracter.

13.1. Utilizando Unicode

Veamos cómo podemos visualizar caracteres mediante Unicode
say "a";
say "\x0061";
say "\c[LATIN SMALL LETTER A]";

Las 3 líneas anteriores muestran formas distintas de construir un caracter:

  1. Escribir el caracter directamente (grafema)

  2. Utilizar \x y el número de código

  3. Utilizar \c y el nombre del número de código

Visualicemos ahora una sonrisa
say "☺";
say "\x263a";
say "\c[WHITE SMILING FACE]";
Otro ejemplo combinando dos números de código
say "á";
say "\x00e1";
say "\x0061\x0301";
say "\c[LATIN SMALL LETTER A WITH ACUTE]";

La letra á puede escribirse:

  • utilizando su número de código único \x00e1

  • o combinando sus números de código de a y la tilde \x0061\x0301

Algunos de los métodos que pueden utilizarse:
say "á".NFC;
say "á".NFD;
say "á".uniname;
Salida
NFC:0x<00e1>
NFD:0x<0061 0301>
LATIN SMALL LETTER A WITH ACUTE

NFC devuelve el número de código único.
NFD descompone el caracter y devuelve el número de código de cada parte.
uniname devuelve el nombre del número de código.

Las letras Unicode pueden utilizarse como identificadores:
my  = 1;
++;
say ;
Se pueden utilizar símbolos matemáticos Unicode:
my $var = 2 + ;
say $var;

14. Paralelismo, Concurrencia y Asincronía

14.1. Paralelismo

En condiciones normales todas las tareas de un programa se ejecutan de forma secuencial.
Esto no suele ser un problema a menos que exista un retardo significativo.

Perl 6 te permite hacer cosas en paralelo.
Llegados aquí, es importante saber que el paralelismo puede tener dos significados:

  • Paralelismo de Tareas: Dos (o más) expresiones independientes ejecutándose en paralelo.

  • Paralelismo de Datos: Una única expresión iterando en una lista de elementos en paralelo.

Comencemos con la última.

14.1.1. Paralelismo de Datos

my @array = (0..50000);                      #Creación del array
my @resultado = @array.map({ is-prime $_ }); #Llama a is-prime por cada elemento del array
say now - INIT now;                          #Visualiza el tiempo que toma el script hasta finalizar
Consideremos el ejemplo anterior:

Hacemos solo una operación @array.map({ is-prime $_ })
La subrutina is-prime es llamada por cada elemento del array de forma secuencial:
is-prime @array[0] después is-prime @array[1] después is-prime @array[2] etc.

Afortunadamente podemos llamar a is-prime en múltiples elementos del array al mismo tiempo:
my @array = (0..50000);                           #Creación del array
my @resultado = @array.race.map({ is-prime $_ }); #Llama a is-prime por cada elemento del array
say now - INIT now;                               #Visualiza el tiempo que toma el script hasta finalizar

Fíjate que la expresión utiliza race. Este método permite la iteración en paralelo de los elementos del array.

Después de ejecutar ambos ejemplos (con y sin race), compara los tiempos consumidos de ambos scripts.

race no guarda el orden de los elementos. Si quieres respetar este orden, utiliza hyper.

race
my @array = (1..1000);
my @resultado = @array.race.map( {$_ + 1} );
@resultado>>.say;
hyper
my @array = (1..1000);
my @resultado = @array.hyper.map( {$_ + 1} );
@resultado>>.say;

Si ejecutas ambos ejemplos verás que uno muestra los resultados ordenados y el otro no.

14.1.2. Paralelismo de Tareas

my @array1 = (0..49999);
my @array2 = (2..50001);

my @resultado1 = @array1.map( {is-prime($_ + 1)} );
my @resultado2 = @array2.map( {is-prime($_ - 1)} );

say @resultado1 == @resultado2;

say now - INIT now;
Veamos el ejemplo anterior:
  1. Definimos 2 arrays

  2. Realizamos una operación con cada array y guardamos los resultados

  3. Y comprobamos si ambos resultados son iguales

El script espera mientras realiza @array1.map( {is-prime($_ + 1)} ) hasta finalizar
y después realiza @array2.map( {is-prime($_ - 1)} )

Ambas operaciones realizadas en cada array son independientes.

¿Por qué no hacer las dos en paralelo?
my @array1 = (0..49999);
my @array2 = (2..50001);

my $promesa1 = start @array1.map( {is-prime($_ + 1)} ).eager;
my $promesa2 = start @array2.map( {is-prime($_ - 1)} ).eager;

my @resultado1 = await $promesa1;
my @resultado2 = await $promesa2;

say @resultado1 eqv @resultado2;

say now - INIT now;
Explicación

El método start evalúa el código y devuelve un objeto de tipo promesa o una promesa (promise).
Si el código se evalúa correctamente, la promesa se cumple (kept).
Si el código lanza una excepción, la promesa se rompe (broken).

El método await espera a una promesa.
Si esta se cumple devuelve los valores.
Si esta se rompe devuelve la excepción.

Comprueba lo que tarda cada script en completarse.

El paralelismo siempre añade una sobrecarga multihilo. Si esta sobrecarga no se compensa aumentando la velocidad de cómputo, el script tardará más en completarse.
Debido a esto, el uso de race, hyper, start y await en scripts muy simples podría ralentizarlos.

14.2. Concurrencia y Asincronía

En https://docs.perl6.org/language/concurrency tienes más información sobre Concurrencia y Programación Asíncrona.

15. La Comunidad

  • En el canal de IRC #perl6 encontrarás el lugar principal para hablar de Perl 6. Consulta: https://perl6.org/community/irc si tienes dudas.

  • Mantente al día con los blogs que tratan de Perl 6 en pl6anet.

  • Suscríbete en el apartado de Perl6 de Reddit clicando en /r/perl6.