Una introducción a Perl 6

Ramiro Encinas

sysadmin de UNICEF Comité Español

Junio 2016

"Las cosas sencillas deberían continuar siendo sencillas, las cosas difíciles deberían ser más fáciles, y las cosas imposibles deberían ser difíciles"
Hay más de una forma para hacerlo.
En Perl 6, decidimos que sería mejor arreglar el lenguaje que arreglar al usuario. "Larry Wall"
Perl 6 debe ser lo suficientemente maleable como para evolucionar a Perl 7, el lenguaje perfecto. Este imperativo darwiniano implica el soporte a múltiples plataformas (por debajo) y a múltiples sintaxis (por encima). "Larry Wall"

Perl 6 es la definición de un lenguaje de alto nivel

  • Dinámico.
  • Modular y escalable.
  • De propósito general.
  • De tipado gradual nativo.
  • Con fuerte soporte en concurrencia componible.
  • Multiparadigma (OO, funcional y procedimental).

Un poco de historia

  • El 24 de octubre del 2000, Larry Wall anuncia el comienzo para escribir Perl 6.
  • Inicio de la especificación mediante el envío de RFCs por parte de los contribuyentes.
  • Apocalipsis: clasificación de Larry Wall de los RFCs.
  • Sinopsis: versión depurada final del Apocalipsis.
  • Exégesis: versión práctica de la Sinopsis.
  • En diciembre de 2015 el Perl Advent Calendar anunció la primera versión estable de Rakudo Perl 6.

Rakudo Perl 6

  • Es el compilador de Perl 6 recomendado. Utiliza MoarVM como backend y nqp ("Not Quite Perl") como entorno para la máquina virtual.
  • Comenzó su desarrollo en 2009.
  • Escrito en C y Perl 6.
  • Versiones para Linux, Windows, FreeBSD, OS X y NetBSD.
  • Disponible en rakudo.org.

Instalación de Rakudo Perl 6

Módulos de Perl 6

  • Actualmente hay más de 600 módulos disponibles desde github.
  • El gestor más popular es Panda.
  • Panda se puede instalar desde:
    • Rakudobrew: rakudobrew build panda
    • Desde su repositorio en github.

Formas de ejecutar Perl 6

  • Escribiendo perl6 en el terminal aparece un prompt donde ejecutar Perl 6 de forma interactiva.
  • Es recomendable que la extensión del script sea .pl6
  • La ejecución tiene el modo Strict por defecto. Para salir del modo estricto: no strict;
  • Dentro de un mismo script permite programar tanto en Perl 5 (mediante use V5;) como en Perl 6 (use V6;).

Comentarios

  • Comenzando con # una línea queda comentada.
  • Comentarios multilínea estilo secciones Pod:

    
    say "Hola";
    =comment
    línea 1
    línea 2
    =cut
    say "Adiós";
    							
  • Comentario incrustado: say #`(comentario) "hola";

Concatenación de strings con ~


my $saludo = "Ho" ~ "la";
say $saludo;
# Hola
					

Adición de strings con ~=


$saludo ~= " y adiós.";
say $saludo;
# Hola y adiós.
					

Sigils

  • El sigil $ es el prefijo de las variables. Puede contener cualquier cosa, pero solo una.
  • El sigil @ es similar a $ pero puede contener una secuencia de cosas e indexarlas.
  • El sigil % es similar a $ pero puede contener varias cosas en la forma par clave-valor (hash).

my @array = 1, 2, 3;
my $a = @array; # $a contiene un solo array
say $a;

[1 2 3]
					

Diseño Orientado a Objetos

  • La clase Mu es la raíz de la jerarquía de tipos de Perl 6. Mu tiene el valor más indefinido que pueda existir en Perl 6. De ella solo heredan las clases Any y Junction.

  • La clase Any hereda de Mu y es la superclase base por defecto de la mayoría de las clases incorporadas y para crear nuevas clases. De ella heredan directamente unas 70 clases.

Métodos interesantes

El método .defined indica si el objeto está definido o no:


say .defined;
False

say "".defined;
True
							

El método .WHAT indica la clase a la que pertenece el objeto:


say .WHAT;
(Any)

"".WHAT;
(Str)

1.WHAT;
(Int)
						

El método ^.methods indica los métodos disponibles:


"x".^methods;
(BUILD Int Num chomp chop pred ...
						

Hay más de 150 métodos y rutinas de texto


	my $nombre = 'Juan De Dios';
	say $nombre.uc;
	say $nombre.chars;
	say $nombre.flip;

	JUAN DE DIOS
	12
	soiD eD nauJ
						

Hay alrededor de 140 métodos y rutinas para enteros


	my $edad = 17;
	say $edad.is-prime;

	True
						

Clase Cool

Es un tipo que puede tratar a su valor como texto o como número:



my Cool $var = 32;
say $var.chars;
say $var * 2;

2
64
					

Definición de tipos restrictivos con subset


subset Coordenada of Real where -1.0 .. 1.0;

my Coordenada $x = 2;  # Type check failed...
					

Ensamblajes (Clase Junction)

  • La clase Junction no proporciona herencia a ninguna clase.
  • Permite filtrar valores sobre un conjunto de ellos.
  • Operaciones any, all, one, none.
  • Soporta funciones.
  • Puede utilizar un hilo por cada elemento, muy útiles con pocos elementos.

Any (cualquier elemento)


my @array = 0, 1, 2;
sub es-cero($x) { return True if $x == 0; }
if es-cero(any @array) { say "Hay un 0"; }

Hay un 0
						

All (todos los elementos)


my @array = 0, 0, 0;
sub es-cero($x) { return True if $x == 0; }
if es-cero(all @array) { say "Todos son 0"; }

Todos son 0
						

Secuencias de elementos

  • El rol Iterable gestiona conjuntos de objetos que pueden recorrerse.
  • La clase List hereda del rol Iterable y del rol Positional, donde éste permite la indexación por posición.
  • Las listas son potencialmente perezosas.
  • La clase Array hereda de la clase List pero sus elementos son escalares ( my @array; ).

Listas perezosas

  • Optimiza el rendimiento y demora su evaluación hasta que es requerida.
  • Permite construir estructuras potencialmente infinitas.
  • Soporta definición de controles de flujo.
  • La pereza se quita con el método .eager

my $listaperezosa = (1 ... 10);
say $listaperezosa;

(1 2 3 4 5 6 7 8 9 10)
						

Lista perezosa con generador deductivo


my $listaperezosa = (0, 2 ... 10);
say $listaperezosa;

(0 2 4 6 8 10)
						

Lista perezosa con generador definido


my $listaperezosa = (0, { $_ + 3 } ... 12);
say $listaperezosa;

(0 3 6 9 12)
						

Lista no perezosa


my $lista = (1 ... 10).eager;
say $lista;

(1 2 3 4 5 6 7 8 9 10)
						

Condiciones

IF

No son necesarios los paréntesis:


if $x == 0 {
	say "El valor es cero.";
}
						

Se puede invertir la condición:


say "El valor es cero." if $x == 0;
						

UNLESS

Es lo contrario de IF:


my $x = 1;
unless $x == 0 {
  say "El valor no es cero.";
}
						

No soporta else.

GIVEN...WHEN

Múltiples condiciones:


my $x = 0;
given $x {
  when $_ == 0 { say "Es cero"; }
  when $_ == 1 { say "Es uno"; }
  default      { say "Ni cero ni uno"; }
}
						

También funciona en solitario:


my $saludo = "hola";
given $saludo { .say; }

hola
						

WITH

Comprueba si la variable está definida:


my Int $x;
with $x { say 'Definida'; }
else { say 'No definida'; }
						

SO

Es un prejijo que devuelve True o False:


my $x = so 1; # $x = True
my $y = so 0; # $y = False
						

También funciona como método:


1.so; # True
0.so; # False
						

Iteraciones

FOR

Recorre una serie de valores proporcionados:


for 1..10 -> $x {
  say $x;
}
						

Puede leer líneas de un fichero de forma perezosa:


for "test.pl6".IO.lines {
  say $_;
}
						

LOOP

Incorpora la parametrización de C para el FOR:


loop (my $x=0; $x < 10; $x++) {
  say $x;
}
						

Bucles infinitos:


loop {
  say "¡Hasta el infinito y más allá!";
}
						

Funciones

Argumentos opcionales con el sufijo ?


sub f ($x?) {
	with $x { say $x; }
	else { say "Sin argumento"; }
}

f;
f "Hola";

Sin argumento
Hola
							

Asignación de argumentos por defecto


sub f ($x=1) {
	say $x;
}

f;
f 2;

1
2
							

Sobrecarga con multi


multi f ($x, $y)     { say $x ~ $y; }
multi f ($x, $y, $z) { say $x ~ $y ~ $z; }

f "U", 2;
f "U", 2, "!";

U2
U2!
						

Restricciones en el tipo de retorno


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

2 al cuadrado es igual a 4
Type check failed for return value; expected Int but got Rat (1.44)
						

Otra forma de hacerlo:


sub cuadrado ($x --> Int) {
							

Programación funcional

Función como argumento de otra función


sub cuadrado($x) {
  return $x ** 2;
}

sub f (&cuadrado) {
  say "El cuadrado de 2 es: " ~ &cuadrado(2);
}

f (&cuadrado);

El cuadrado de 2 es: 4
						

Función asignada a variable


sub cuadrado($x) {
  return "El cuadrado de $x es " ~ $x ** 2;
}

my $x = cuadrado(2);

say $x;

El cuadrado de 2 es: 4
						

Devolución de una función (Clausura)


sub cuadrado($x) {
	my $y = $x ** 2;
  return sub { say "El cuadrado de $x es $y" };
}

my $resultado = cuadrado(2);
$resultado();

El cuadrado de 2 es 4
						

Funciones anónimas


my $cuadrado = -> $x { $x ** 2; }
say $cuadrado(9);

81

my $cuadrado = * ** 2;     # lo mismo que -> $x { $x ** 2; }
say $cuadrado(9);

81
						

Encadenamiento


sub cuadrado ($x) { return $x ** 2; }
sub cubo ($x)     { return $x ** 3; }

my $num = 2;
say $num.&cuadrado.&cubo;

64
						

Hiperoperador >>.

Aplica una función a una serie de elementos:


my @array = <0 1 2 3 4 5 6 7 8 9 10>;
sub cuadrado($x) { return $x ** 2 };

say @array>>.&cuadrado;

[0 1 4 9 16 25 36 49 64 81 100]
						

Los ensamblajes y listas perezosas también forman parte de la programación funcional.

Entrada/Salida

Acceso a parámetros del script


say @*ARGS;     # todos los parámetros
say @*ARGS[0];  # el primer parámetro
						

Entrada por teclado


print "Nombre: ";
my $nombre = get;
my $edad = prompt("Hola $nombre, dime tu edad: ");
say "No aparentas tener $edad años.";
						

Ejecución de comandos del sistema

  • run ejecuta un comando externo sin tener en cuenta la shell.
  • shell ejecuta un comando externo tal y como lo haría la shell.

Lectura de archivos de texto con open


my $f = open("data.txt");
say $f.lines; # lista perezosa con las líneas
$f.seek(2); # vuelve al caracter 2
for $f.lines -> $linea { say $linea; } # recorre desde el caracter 2
$f.close;
						

Iteración directa línea a línea


for "data.txt".IO.lines -> $linea { say $linea; }
						

Lectura de archivos de texto con slurp


my $texto = slurp "data.txt";
say $texto; # muestra el archivo tal cual
say $texto.lines; # lista perezosa con las líneas
						

Escritura de archivos de texto con open


my $f = open "data2.txt", :w;
$f.print("linea 1\n");
$f.say("línea 2");
$f.close;
						

Escritura de archivos de texto con spurt


spurt "data2.txt", "línea 1\nlínea 2\n";
						

Gestión de directorios del sistema


say dir; say dir "/usr/bin";
mkdir "nueva-carpeta";
rmdir "nueva-carpeta";
						

Comprobación de directorios y archivos


"nombre-archivo-carpeta".IO.e; # comprueba la existencia
"nombre-archivo-carpeta".IO.d; # comprueba si es directorio
"nombre-archivo-carpeta".IO.f; # comprueba si es archivo
						

Clases y Objetos

  • Las clases se definen con Class.
  • Las clases utilizan atributos y métodos tanto públicos (mediante el twigil .) como privados (mediante el twigil !).
  • Los métodos por defecto son públicos.
  • La herencia entre clases se realiza con la característica is.

Ejemplo básico de Clase y un objeto


class Clase {
  has $!var-privada;
  has $.var-publica is rw;
  has @!array-privado;

  method modificar($var) { $!var-privada = $var; }
  method agregar($var) { push @!array-privado, $var; }
  method ver-privado { say $!var-privada; say @!array-privado; }
}

my $objeto = Clase.new(var-publica => "valor1");

say $objeto.var-publica;
$objeto.var-publica = "valor2";
say $objeto.var-publica;
$objeto.modificar("valor3");
$objeto.agregar("valor4");
$objeto.agregar("valor5");
$objeto.ver-privado;

valor1
valor2
valor3
[valor4 valor5]
						

Herencia simple


		class ClaseHija is ClasePadre {
			method foo {...}
		}
							

Si un método heredado de la clase padre tiene el mismo nombre que un método definido en la clase hija, prevalece el método de la clase hija.

Herencia múltiple


	class ClaseHija is ClasePadre is ClaseMadre{
		method foo {...}
	}
						

La resolución de varios métodos con el mismo nombre se realiza mediante el algoritmo C3.

Roles

  • Son parecidos a las clases en cuanto a que son una colección de atributos y métodos.
  • Una clase puede heredar de varios roles que contengan métodos con el mismo nombre, permitiendo elegir el método heredado adecuado y así evitar el conflicto.
  • Los roles se heredan mediante la característica does.

Ejemplo de herencia múltiple de roles:


role RolPadre {
  method habla($mensaje) { say $mensaje ~ " al padre." ; }
}

role RolMadre {
  method habla($mensaje) { say $mensaje ~ " a la madre."; }
}

class ClaseHija does RolPadre does RolMadre {
  method habla ($mensaje) {
    self.RolMadre::habla($mensaje);
  }
}

my $objeto = ClaseHija.new;
$objeto.habla("Hola");

Hola a la madre.
						

Expresiones Regulares

  • El operador de coincidencia es ~~ y el inverso es !~~
  • Los modificadores se ubican a la izquierda del patrón precedidos cada uno por dos puntos (m:g:i:s/patrón/).
  • Los espacios en blanco no se tienen en cuenta. Para tenerlos en cuenta se utiliza el modificador :s
  • Los caracteres alfanuméricos y el guión bajo coinciden tal cual, el resto necesita escaparse con la barra invertida.
  • Los elementos encontrados se ubican en la lista $/ cuyos elementos son de tipo match.

Grupos de captura

Cada elemento encontrado (tipo match) incluye otro array con los grupos de captura:


"abcdefghijk" ~~ m:g/bc(.)efg(.)ij/;
# el modificador :g busca todas las coincidencias

say $/[0][0];     # tipo match;
say $/[0][1].Str; # tipo String;

「d」
h
						

Concurrencia

Promesas

Ejecución de código en hilos independientes:


sub uno{ sleep 5; return "uno!"; }
sub dos{ sleep 10; return "dos!"; }

my $p1 = Promise.start( { uno } );
my $p2 = Promise.start( { dos } );

say await $p1, $p2;
						

Se crean tres hilos, uno para el proceso principal, otro para una promesa y otro para la otra. Transcurren 10 segundos y visualiza: (uno! dos!)

Supplies

  • Gestiona eventos a tiempo real de forma concurrente.
  • El método emit lanza un evento.
  • El método tap recoge eventos a tiempo real.
  • Las clases relacionadas con E/S como IO::Socket:Async suelen proporcionar un supply como método y realizan los emit correspondientes.

Se emiten tres eventos en distintos momentos y tap los recoge a tiempo real:


my $supply = supply {
  for 1..3 -> $x {
    sleep $x;
    emit "Han transcurrido $x segundos";
  }
}
say "Inicio";
$supply.tap( -> $r { say $r });

Inicio
Han transcurrido 1 segundos
Han transcurrido 2 segundos
Han transcurrido 3 segundos
						

Ejemplo de programación reactiva

Servidor web multihilo con react y whenever:


my $cabecera-http = qq{HTTP/1.1 200 OK
Content-Type: text/html;charset=utf-8

};
my $mensaje-respuesta = "¡Hola mundo!";

react {
  whenever IO::Socket::Async.listen('localhost',3000) -> $conn {
    whenever $conn.Supply -> $buf {
      $conn.print: $cabecera-http ~ $mensaje-respuesta;
      $conn.close;
    }
  }
}
						

Conclusiones

  • Perl 6 es tan completo y extenso que lo tendremos hasta por lo menos los próximos 100 años como mínimo.
  • La jerarquía de clases permite una modularidad, escalabilidad y retrocompatibilidad muy aprovechable en el futuro.
  • En muchos aspectos concretos se escribe menos código en comparación con otros lenguajes.
  • Rakudo tiene que seguir madurando, entre otras cosas, para conseguir un rendimiento a la altura que Perl merece.

¡Gracias!