Le langage Perl était au départ très modeste et a grossi en respectant la compatibilité ascendante. Créé en 1987, Perl a accumulé beaucoup de scories qui rendent son apprentissage difficile et complexifient inutilement l'interpréteur. Il fallait s'en débarrasser. Les comportements par défaut du langage étaient conçus pour des programmes de quelques lignes. Même si le langage avait évolué (variables lexicales, orientation objet...) pour supporter de gros programmes, une grande partie des programmeurs n'avaient pas adapté leur style, ce qui a donné à Perl une réputation de langage sale.
L'interpréteur de Perl 5 n'utilise pas au mieux les architectures multiprocesseurs ou multi cœur dont la mémoire est lente par rapport au processeur. De plus, pour la programmation en grand, il est nécessaire d'avoir un système puissant de passage de paramètres. De même il était important de donner la possibilité au programmeur, mais non l'obligation, d'un typage statique des données. Ce typage constitue à la fois une documentation, une forme d'assertion exécutée à la compilation et une amélioration des performances par rapport au typage dynamique.
Perl 6 garde l'esprit de Perl 5. La plupart des changements apportés ont pour but de rendre le langage plus homogène, d'intégrer de nouvelles fonctionnalités et de mieux exploiter les architectures matérielles modernes. Les nouvelles fonctionnalités sont un puissant système de passage de paramètres, la curryfication , le support du typage statique (mais son utilisation reste optionnelle), l'évaluation paresseuse, l'expression du parallélisme dans le code source pour utiliser au mieux les multiprocesseurs ou les processeurs multi cœur, et, optionnellement, l'inférence de types. Le dispatch multiple et le mécanisme puissant de liage d'arguments permettent notamment de supporter des fonctionnalité similaire au filtrage par motif de la programmation fonctionnelle.
Le but est, selon Larry Wall, de rendre les choses simples plus simples et les choses compliquées plus facilement réalisables.
En Perl 5, la référence est la mise en œuvre unique du langage. Si la documentation et la mise en œuvre divergent, sauf bug avéré, c'est la documentation qui doit être amendée. Ce système n'est plus possible car il semble que Perl 6 aura au moins deux mises en œuvre. De plus, pour une mise en œuvre donnée, il pourra y avoir des générateurs de code réalisés par des tierces parties.
Perl 6 est spécifié par des documents appelés synopsis. Ils ont été précédés par des apocalypses (au sens de révélation) qui expliquaient et motivaient les premières décisions. Les apocalypses ont maintenant une valeur purement historique.
Le système statique de typage de Perl 5 comporte peu de types. Lors de la déclaration d'une variable, son sigil détermine si elle est de type scalaire, tableau ou hash (métonyme pour table associative). Un scalaire peut contenir un entier, un flottant ou une chaîne de caractères. Puisque le type du contenant ne détermine que partiellement le type du contenu, on parle de typage semi-dynamique.
En Perl 6, les sigils et le typage dynamique de Perl 5 ont été étendus par l'addition de types statiques. Cela consiste en la déclaration explicite du type du contenant. Par exemple :
my int $i = 0; my num $n = 3.141; my str $s = "Hello, world";
Cependant, tout comme en Perl 5, les programmeurs peuvent se passer de typage explicite :
my $i = "25" + 10;
Le typage statique est une forme de documentation et de tests intégrée au code source. Il améliore la maintenance, spécialement dans les grands projets logiciels. Mais le typage statique alourdit le code de scripts courts ou d'unilignes. Ce style concis sans déclaration explicite de type autre que par les sigils est une force du Perl lorsque utilisé pour l'écriture de code à usage unique.
Perl 6 introduit aussi des sigils secondaires appelés twigils.
Perl 5 définit les sous-routines sans liste formelle de paramètres.
Les arguments de sous-routines entrant dans une sous-routine devenaient des alias dans les éléments du tableau @_. Si @_ était modifié, les changements étaient reflétés dans les données originales :
# Code Perl 5 sub incr { $_[0]++ } my $x = 1; incr($x); # $x est maintenant 2 incr(3); # erreur de [[moteur d'exécution|runtime]]: "Essai de modification d'une valeur en lecture seule"
Note: Le système de prototypes, ajout tardif au langage Perl 5, est un moyen de vérification du nombre de paramètres et une faible vérification de type.
Perl 6 introduit un vrai formalisme de paramètres dans le langage qui va au delà des signatures de types. En Perl 6, une déclaration de sous-routine ressemble à :
sub faire_quelquechose(Str $chose, Int $autre) { ... }
Comme en Perl 5, les paramètres formels (exemple, les pseudo-variables dans la liste de paramètres) sont aliasés en leurs paramètres effectifs (valeurs d'entrées), mais par défaut, les alias sont marqués is readonly signifiant qu'ils sont en lecture seule et donc constants :
sub incr(Num $x) { $x++; # compile-time error }
Si un paramètre formel est suivi par is copy ou is rw, cependant, il peut être modifié. Dans le cas is copy, Perl 6 copie les paramètres actuels plutôt que des les aliaser ; ainsi ils peuvent être modifiés, mais les changements restent locaux à la sous-routine. Dans le cas is rw (rw signifie lecture-écriture, read-write en anglais), l'alias n'est pas marqué readonly. Ce changement détecte aussi, lors de la compilation, des erreurs telles que :
sub incr(Num $x is rw) { $x++ } incr(3); # erreur au moment de la compilation sub incr_copy(Num $x is copy) { $x++ } incr_copy(3); # Pas de problème
De nouvelles fonctionnalités de Perl 6 dans les listes de paramètres rendent le passage de paramètres beaucoup plus puissant qu'en Perl 5 :
Par exemple :
sub my_split(Rule $pat? = rx/\s+/, Str $expr? = $_, Int $lim? where $^lim >= 0) { ... }
Perl 6 supporte aussi la curryfication.
En Perl 5, le sigil — le caractère non alphanumérique qui précède un nom de variable — pouvait changer selon son contexte d'utilisation (scalaire ou tableau) :
# Perl 5 code my @array = (0, 1, 2, 3); my $element = $array[1]; # $element est égal à 1
En Perl 6, l'abondance des contextes possibles nécessite un mécanisme différent des sigils. Le sigil ne désigne plus le contexte d'accès. Perl 6 propose des opérateurs spécialisés pour ce faire. Le sigil d'une variable est donc invariant. Il est plus pédagogique d'avoir deux mécanismes syntaxiques pour deux fonctions grammaticales différentes (le typage faible de la variable et son contexte d'accès).
# Perl 6 code my @array = (0, 1, 2, 3); my $element = @array[1]; # $element est égal à 1
Perl 5 supportait l'orientation objet via un mécanisme propre à Perl et nommé bénédiction. N'importe quelle référence pouvait être bénie comme étant un objet d'une classe particulière, comme :
# Perl 5 code my $object = bless $reference, 'Class';
Un objet béni pouvait alors avoir des méthodes invoquées en utilisant la « syntaxe flèche » :
# Perl 5 code $objet->méthode();
L'invocation identifie la sous-routine appropriée de nom méthode
, et l'appelle avec $objet
comme premier argument.
Bien que très puissant — virtuellement n'importe quel autre modèle objet d'un autre langage pouvait être simulé en utilisant cette simple fonctionnalité — il rendait le cas le plus commun d'orientation objet, comme une structure C associée à du code, inutilement difficile. De plus, Perl ne pouvant faire d'hypothèse sur le modèle objet utilisé, l'invocation de méthode ne pouvait pas être très bien optimisée.
Dans l'esprit de rendre les choses simples plus simples, et les choses compliquées faciles, Perl 6 garde le principe de bénédiction pour créer une instance pour les programmeurs qui désirent des fonctionnalités communes. Cela fournit un modèle objet plus robuste pour les cas communs. Par exemple, une classe pour encapsuler un point cartésien peut être écrite comme :
class Point is rw { has $.x; has $.y; }
et utilisée :
my Point $point .= new; $point.x = 1.2; $point.y = -3.7;
Le point remplace la flèche (propre à Perl 5) comme opérateur d'accès au membre d'une instance. C'est la syntaxe propre à de nombreux langages dont C++, Java, Python, et Ruby.
Notez que les méthodes « x
» et « y
» ne sont pas déclarées explicitement. Elles sont appelées des auto-accesseurs. Le modificateur « is rw » dans la définition de la classe permet à tous ses attributs publics d'être écrits par défaut, en utilisant les auto-accesseurs.[1]
Les données membres d'une instance sont appelés « attributs ». Elles peuvent être déclarées ainsi :
has $.a; # mode d'accès par défaut (généralement en lecture seule) has $.b is rw; # accès en lecture et en écriture has $!c; # membre privé; pas d'accesseur public has $d; # comme $!d
Dans une même portée, on peut définir plusieurs routines ou méthodes du même nom en les préfixant par le mot-clé multi. On parlera de multi pour désigner l'ensemble des routines ou méthodes de même nom. Lors de l'appel d'une multi, la routine ou méthode dont la signature correspond aux arguments passé sera appelé. Le système de liage entre les arguments de l'appel et les paramètres de l'appelé est très pointu.
Ainsi, pour traverser un arbre n-aire, l'argument attendu est soit un nœud de type Nary dont les enfants sont un tableau commençant par l'$ainé et suivi éventuellement de @frères, soit une $feuille.
multi traverser ( NAry $noeud ( :enfants [$ainé, *@frères] ) ) { traverser($ainé); traverser(:enfants(@frères)); # (lie @frères à $sommet) } multi traverser ( $feuille) {...}
Ce système de multiméthodes inclut donc la fonctionnalité de filtrage par motif propre à la programmation fonctionnelle.
Les expressions rationnelles (en anglais, regex, pour regular expression) de Perl ont connu tellement de succès qu'elles ont été mises en œuvre par une bibliothèque appelée PCRE (Perl Compatible Regular Expressions). Via PCRE, les expressions rationnelles ont été incluses sans amélioration dans beaucoup d'autres langages. Pourtant, comme le fait remarquer Larry Wall, ce sont les expressions rationnelles qui ont contribué à donner à Perl une réputation de langage peu lisible. Elles sont trop compactes et trop malignes, les mêmes caractères sont utilisés pour des usages divers. Il y a peu de support pour les captures nommées, peu de support pour les grammaires, et une intégration pauvre avec les langages réels.
Perl 6 fournit un sur-ensemble des fonctionnalités de Perl 5 concernant les expressions rationnelles. Le mécanisme des regex fournit une puissance comparable aux analyseurs syntaxiques. Ces regex agissent comme des fermetures par rapport à leur champ lexical. Les regex sont introduites avec un des mots-clefs rule
, regex
, token
. La définition d'une regex est similaire à une définition de sous-routine et peut admettre des paramètres. Le choix du mot-clef permet de contrôler si les espaces sont significatifs ou non dans la regex, et de spécifier s'il peut y avoir retour sur trace ou non. Comme en Perl 5, on peut aussi définir des regex anonymes ou les utiliser directement dans les opérateurs m
(matching) ou s
(chercher et remplacer).
Seules six fonctionnalités n'ont pas été changées depuis les regex du Perl 5 :
(...)
|
\
*
, +
, et ?
*?
, +?
, ??
Quelques-uns des ajouts les plus efficaces sont :
pour construire des grammaires entières.Les changements suivants ont grandement augmenté la lisibilité des regexes :
[...]
qui sont les mêmes que ceux du Perl 5's : (?:...)
{...}>
/x
de Perl 5 est maintenant le défaut.Exemples :
rx { a [ b | c ] ( d | e ) f : g } rx { ?( ab* ) <{ $1.size % 2 == 0 }> }
La dernière ligne est identique à :
rx { ( ab[bb]* ) }
Les parenthèses des prédicats de contrôle de flot en Perl 5 sont maintenant optionnelles :
if is_true() { for @array { ... } }
Les trois points ci-dessus (...
) sont syntaxiquement valides en Perl 6 et constituent l'opérateur « yadda-yadda ». « ...
» peut être utilisé comme une marque substitutive pour du code qui doit être inséré par la suite. Si un programme essaye d' exécuter « ...
», cependant, une exception est levée. Cet opérateur est utile pour les méthodes abstraites, ou bien pour marquer les places où le programmeur essaye d'insérer du code plus tard.
Les nouveaux programmeurs attendent souvent que les comparaisons chaînées
comme dans l'uniligne ci-dessous fonctionnent :
if 1 <= $dé1 == $dé2 <= 6 { say "Doubles!" }
En Perl 6, ce code fonctionne maintenant naturellement, dans l'esprit du DWIM (Do What I Mean), et s'exécute comme :
if 1 <= $dé1 and $dé1 == $dé2 and $dé2 <= 6 { say "Doubles!" }
Perl 6 propose l'évaluation paresseuse de listes qui est une fonctionnalité de certains langages de programmation fonctionnelle tels que Haskell. L'évaluation paresseuse simplifie des tâches communes en Perl6 comme les opérations d'entrées/sorties, la transformation de listes et le passage d'arguments à une routine :
my int @integers = 0..Inf; # entiers de 0 à l'infini
Le code ci-dessus ne crashera pas en essayant d'assigner une liste de taille infinie à la table @integers
.
Perl 6 introduit le concept de jonctions. Nous choisissons ce terme pour le distinguer des jointures, concept propre aux bases de données relationnelles. Les jonctions sont des valeurs scalaires composites. Les jonctions ont été initialement appelées superpositions, par analogie au concept de physique quantique de superpositions quantiques — des courbes qui peuvent simultanément occuper plusieurs états jusqu'à ce que leur observation les effondre. Un module Perl 5 réalisé en l'an 2000 par Damian Conway appelé Quantum::Superpositions
fournissait une preuve de concept initiale. D'abord une curiosité programmatique, les jonctions sont un concept important de Perl 6.
Dans leur forme la plus simple, les jonctions sont créées par combinaison d'un ensemble de valeurs avec l'opérateur de jonction :
my $even_digit = 0|2|4|6|8; # any(0, 2, 4, 6, 8) my $odd_digits = 1&3&5&7&9; # all(1, 3, 5, 7, 9) my $not_zero = none(0);
Ces valeurs peuvent être utilisées arithmétiquement :
my $jonction = 1|2|3; $jonction += 4; # jonction maintenant égale à 5|6|7 $jonction += (1&2); # jonction maintenant égale(6|7|8)&(7|8|9)
ou dans des comparaisons :
if $grade eq any('A'..'D') { say "pass" }
ou même pour l'accès à un tableau :
if %person{any('first_name', 'nickname')} eq "Joe" { say "What do you know, Joe?" }
Les jonctions peuvent aussi être utilisées pour étendre le système de types :
class RGB_Color is Tuple[int, 3] & Color { ... } sub get_tint (RGB_Color|CMYK_Color $color, num $opacity where 0 <= $^opacity <= 1) { ... } sub store_record (Record&Storable $rec) { ... }
Les jonctions ne sont pas ordonnées ; 1|2|3
et 3|2|1
représentent les mêmes valeurs. Cette absence d'ordre signifie que le compilateur Perl 6 peut choisir d'évaluer les expressions jonctives en parallèle. En fait, plusieurs dans la communauté Perl 6 croient que les jonctions peuvent surpasser l'explicite multithreading comme manière ordinaire d'accomplir le parallélisme en Perl 6. Par exemple, le code
for all(@array) { ...}
pourrait indiquer au compilateur que la boucle for
doit être exécutée en parallèle plutôt qu'en série.
Dans les langages de bas niveau, le concept de macros a été synonyme de substitutions textuelles du code source à cause de la large utilisation d'un préprocesseur ignorant la syntaxe du langage. Le langage C utilise la construction #define
. Perl 5 utilise des systèmes de filtres sources à cet effet. Ils sont notoirement peu fiables, car leur empilement a des effet aléatoires. En effet chaque filtre suppose que le code qu'il filtre est du code Perl 5 alors qu'il reçoit du code modifié par ces prédécesseurs.
Lisp est privilégié, car il propose un système de macros qui manipulent directement l'arbre syntaxique correspondant au source du programme. C'est facile car sa syntaxe concrète est identique à sa syntaxe abstraite.
Perl 6 proposera les deux types de macro: substitutions textuelles et manipulation de l'AST.
Une définition de macro Perl 6 ressemblera à une définition de sous-routine ou méthode, et peut travailler sur des chaînes non analysées, un arbre de syntaxe abstrait AST représentant du code pré-analysé, ou encore une combinaison des deux. Une définition de macro pourrait ressembler à :
macro hello($what) { q:code { say "Hello { {{{$what}}} }" }; }
Dans cet exemple particulier, la macro n'est pas plus complexe qu'une substitution textuelle en C, mais parce que l'analyse du paramètre de la macro se produit avant que la macro n'opère sur le code appelé, les messages de diagnostic seront beaucoup plus informatifs. Cependant, parce que le corps de la macro est compilé à chaque lancement du programme, diverses techniques d' optimisation peuvent être employées.