Sommaire
Bonjour Nal,
je vais te parler d'un langage que je me suis mis à apprendre ce mois d'août. Son nom c'est J tout court. Il s'agit d'un langage spécialisé dans la manipulation de tableaux multidimensionnels à l'aide d'une notation très compacte, descendant d'APL. Il n'y a qu'une seule implémentation, dont la version 701 a été libérée sous GPLv3 en 2011, mais des licences commerciales sont aussi possibles. Il s'agit d'un langage interprété, pensé pour une utilisation intéractive exploratrice même s'il permet aussi une certaine modularité pour faire des programmes plus gros.
Soyons honnêtes, au début on a parfois l'impression qu'il s'agit juste de code offusqué, ou d'un simple jeu. Pour dire, j'ai failli renoncer malgré mon habitude des notations en tant qu'étudiant mathématicien. Cependant, une fois habitué à la philosophie du langage j'ai fini par changer d'avis, d'où ce journal :)
Un petit tour avec J
Maintenant je vais essayer de donner un petit avant-goût de cet étrange langage.
Un peu d'arithmétique
C'est parti :
2 + 3
5
2 4 + 1 3
3 7
+
se généralise donc tout seul aux listes, mais aussi à un tableau de n'importe quelle dimension. Et la plupart des fonctions se généralisent de façon analogue:
2 ^ 0 1 2 3 4
1 4 8 16
Alors bon, c'est bien tout ça, mais d'autres langages comme Scilab ou Octave permettent des choses similaires. Là où J se démarque vraiment c'est dans sa grammaire exotique.
Tout d'abord, l'évaluation est extrêmement simple : pas de priorité parmi les opérateurs, +
et *
sont logés à la même enseigne :
2 * 2 + 3
10
Il faut donc lire 2 * ( 2 + ( 3 ) )
. On peut bien sûr mettre des parenthèses pour changer le destin :
(2 * 2) + 3
7
Du vocabulaire
Et là c'est la surprise : au lieu de parler de variables ou fonctions, on parle de noms et de verbes. En fait, on rencontre des adverbes, des conjonctions, et même des gérondifs! Pourquoi ces noms fantaisistes? Parce qu'un programme J est composé de phrases et que ce vocabulaire donne une image assez fidèle de la réalité.
Adverbes, conjonctions
Regardons l'adverbe /
pour se faire la main:
+/ 1 2 3 NB. 1 + 2 + 3 (après NB. c'est un commentaire)
6
On l'appelle « insère », et il s'agit bien de ça, il insère le verbe qui le précède entre les éléments. Ici 1 + 2 + 3
. On peut l'appliquer à n'importe quel verbe. Prenons par exemple <.
qui renvoie le plus petit parmi deux nombres.
3 <. 2
2
<./ 5 7 3 4
3
En gros, un adverbe est une fonction qui prend un verbe en argument et renvoie un nouveau verbe avec un nouveau comportement. Les conjonctions sont similaires mais prennent deux verbes ou noms en argument. Par exemple @:
sert à composer deux verbes. Donc avec >:
verbe qui incrémente de un les éléments d'un tableau, */ @: >:
est le verbe qui incrémente tout de un puis fait le produit des éléments.
Verbe monadique/dyadique
Jusqu'à présent, on a rencontré des cas de verbes dyadiques, c'est-à-dire qui prennent deux arguments. La plupart des verbes ont aussi un cas monadique, pour <.
par exemple:
<. 2.3 NB. partie entière : l'entier inférieur le plus proche
2
Comment définir ses propres verbes, noms,…
Le symbole =:
qui permet d'assigner un nom, verbe, adverbe ou conjonction. Par exemple min =: <./
pour définir le verbe minimum, qu'on utilise simplement min 5 7 3 4
comme n'importe quel verbe.
Séquences de verbes (« verb trains » en anglais)
En J, une succession de deux ou trois verbes ou plus à la suite a un sens particulier. Un exemple classique avec trois verbes : calcul de la moyenne d'une liste de nombres. Le verbe #
renvoie la longueur d'un tableau, et %
est la division (/
est déjà pris!) :
moy =: +/ % #
moy 2 9 7 NB. (2 + 9 + 7) % 3
6
Où est la magie? Il s'agit d'une fourchette (fork) : si f, g et h sont trois verbes et y un nom, (f g h) y
signifie (f y) g (h y)
. Ici on doit donc lire « la somme(f) divisée(g) par la longueur(h) ». Ces séquences permettent un style de programmation dit tacite, où on définit des fonctions sans faire référence explicitement aux arguments.
Le rang
Une notion importante : celle du rang (ou dimension). Dans les cas les plus simples il s'agit de décider si une somme se fait suivant les lignes ou les colonnes, par exemple:
a =: i. 3 3 NB. matrice 3x3 avec les 9 premiers entiers
a
0 1 2
3 4 5
6 7 8
NB. + est « inséré » entre les lignes.
+/ a
9 12 15
NB. somme suivant chaque ligne (dimension 1)
NB. On utilise la conjonction " appelée « rang »
+/"1 a
3 12 21
Aller plus loin
Voilà, il reste une multitude de choses : calculs d'inverses, itérations à l'aide de la conjonction ^:
, structures de contrôles (if-then-else,… et même while !),… mais l'esprit est là, il ne reste plus qu'à se plonger dans un des guides du wiki :)
Quelques remarques en vrac
Tout a l'air assez simple (j'espère), mais il y a un hic quand on apprend J : il y a plus d'une centaine de primitives (verbes, adverbes, conjonctions), qui s'écrivent à l'aide d'une seule lettre ou symbole, suivie parfois d'un ou deux .
ou :
, et même si souvent c'est mnémotechnique, pas toujours, et il m'a fallu un paquet d'heures pour les digérer avant de pouvoir comprendre le code des autres. Le pire c'est les fonctions pour intéragir avec le monde extérieur, qui s'obtiennent toutes grâce à la conjonction !:
, par exemple 1!:1
est un verbe pour lire un fichier, aucun moyen de s'en souvenir, même si heureusement pas mal d'alias plus verbeux sont prédéfinis pour les tâches courantes.
Malgré les difficultés, c'est un langage sympathique, et la possibilité d'exprimer des algorithmes assez complexes, demandant dans un langage plus traditionnel plusieurs boucles, en une seule ligne, c'est quand même pratique. Bien sûr, il faut être capable de se relire après, et je n'y croyait qu'à moitié au début, mais c'est tout à fait possible en fait avec de l'entraînement. Un autre avantage est que toutes les opérations sur les tableaux sont très optimisées, donc on se retrouve avec du code très rapide à la fin, du moment qu'on applique les fonctions sur un maximum de données à la fois (pas de problèmes pour effectuer des opérations sur des tableaux avec des millions de valeurs).
La documentation du langage est assez particulière, puisqu'il s'agit d'un «dictionnaire», et le wiki officiel collectionne assez d'articles intéressants, et guides variés.
Une remarque concernant la licence : la version 701 de J est GPLv3, mais les binaires distribués sur le site officiel par contre viennent avec quelques autres trucs non libres (un IDE, des docs et des broutilles), donc il faut compiler soi-même. Sous Gentoo il y a un ebuild, mais j'ai pas l'impression qu'il y ait de package Debian, sans doute parce que le makefile n'est pas terrible, d'ailleurs l'ebuild Gentoo doit placer ça dans /opt/. Il y a aussi un petit problème mineur : le binaire s'appelle par défaut jconsole donc petit conflit de noms avec java.
La compilation est assez facile, il y a juste readline qui n'est pas activé par défaut : le plus simple c'est d'utiliser rlwrap.
Enfin, une petite mise en garde : apprendre J peut avoir des conséquences surprenantes, comme en témoigne ce petit extrait du fichier vt.c
du code source C de l'interpréteur définissant le cas monadique du verbe {.
qui renvoie le premier élément d'un tableau :
F1(jthead){I wcr,wf,wr;
RZ(w);
wr=AR(w); wcr=jt->rank?jt->rank[1]:wr; wf=wr-wcr;
R !wcr||*(wf+AS(w))? from(num[ 0],w) :
SPARSE&AT(w)?irs2(num[0],take(num[ 1],w),0L,0L,wcr,jtfrom):rsh0(w);
}
C'est du C ça!? De quoi faire frémir Linus Torvalds ;)
# euh?!
Posté par Nicolas Boulay (site web personnel) . Évalué à 2.
Si jamais je vois un codeur qui utilise "*/ @: >:" comme code, je le pends par les c…
/ c'est map
f @: g c'est x -> f (g(x))
C'est quand même plus lisible !
"La première sécurité est la liberté"
[^] # Re: euh?!
Posté par anaseto . Évalué à 3.
Imagine f @: g @: h @: k, et compare avec f(g(h(k(x)))). Ça devient moins clair que c'est plus lisible. D'ailleurs en maths pour la composition on utilise un rond, et certains langages comme Haskell proposent un opérateur pour la composition (Ocaml aussi dans les batteries).
Je te préviens, en J tu peux faire bien pire que ça ;)
[^] # Re: euh?!
Posté par Nicolas Boulay (site web personnel) . Évalué à 10.
Pourtant perl était un bon exemple à ne pas reproduire.
Un code est bien plus souvent lu qu'écrit, gagner du temps à l'écriture, fait perdre un temps de malade ensuite.
"La première sécurité est la liberté"
[^] # Re: euh?!
Posté par Zylabon . Évalué à 4. Dernière modification le 23 août 2013 à 15:04.
C'est une question d'habitude. Pour qu'un code fonctionnel soit lisible, il faut qu'il le soit pour quelqu'un qui est habitué à un code fonctionnel, pas impératif.
J'aime bien le codé visuel de la composition de fonction, quand on écrit
f (g (h x))
on le lit « f appliqué à g appliqué à h(x)", en écrivantf . g . h x
on fait apparaitre le fait que f g et h sont combiné pour faire une nouvelle fonction, ça permet de on retrouver un peu le coté procédural, tout en étant plus compacte :petit ajout : ça dépend aussi de la syntaxe du langage, en pseudo lisp, il est évident que
(f (g (h x)))
est moins lisible que((compose f g h) x)
Please do not feed the trolls
[^] # Re: euh?!
Posté par anaseto . Évalué à 3.
Les symboles vs les mots c'est une question de fréquence d'utilisation (personne ne se plaint du + ou du * par exemple), et avec J tu passes ton temps à utiliser les primitives donc la plupart rentre dans la catégorie « usage fréquent ». Certaines non, et c'est dommage, mais rien n'empêche d'un autre côté de se créer des alias : les fonctions trigonométriques utilisent la primitive
o.
, je n'arrive pas à me souvenir de son utilisation donc je me suis fait des alias pour cos, sin, etc…Au moins avec J tu n'as quasiment pas de syntaxe :
=:
et=.
pour l'assignement, et les parenthèses et c'est à peu près tout, le reste sont des fonctions qui suivent les règles du langage.[^] # Re: euh?!
Posté par Nicolas Boulay (site web personnel) . Évalué à 2.
"La première sécurité est la liberté"
[^] # Re: euh?!
Posté par anaseto . Évalué à 5.
Je ne dis pas que quelqu'un qui connaît C (ou autre) va trouver ces symboles intuitifs, mais que quelqu'un qui programme en J finit par les connaître, et c'est l'important.
Le
/
vient d'APL ou la division utilisait÷
, mais le problème avec les symboles non-ascii d'APL c'était qu'ils n'étaient pas pratiques à taper sur un clavier, mêmes s'ils étaient plus intuitifs et traditionnels (mathématiquement parlant).%
était le symbole qui se rapprochait le plus et donc mnémotechnique pour les APListes.Ça prend un peu de temps, mais comprendre à fond la portée des primitives, leur sens, ce qu'elles font suivant leur rang qui détermine la manière dont elles agissent sur un tableau, les diverses façons de les composer, etc… prend beaucoup plus de temps que de se faire à la notation, et ce n'est finalement pas si important que ça: la plupart du temps où je restais bloqué pour comprendre un expression ce n'était pas parce que je ne me souvenais pas du nom informel du symbole (comme 'insert' pour /), mais plutôt de la façon exacte dont il se comportait par rapport à ses arguments, et que
/
s'appelle fold ou reduce ne change rien.Et puis il faut garder à l'esprit l'objectif principal du langage : une notation pour l'exploration intéractive d'algorithmes, donc c'est important de pouvoir faire loger suffisamment d'information sur une seule ligne. Il ne s'agit pas vraiment de faire de gros programmes, ni des systèmes critiques. Je ne sais pas si c'est possible, je n'ai encore fait aucun programme de plus d'une trentaine de lignes en J.
Alors c'est vrai, c'est un langage un peu surprenant, et je réagissais pareil au début, mais je suis content de ne pas être passé à côté finalement.
[^] # Re: euh?!
Posté par lasher . Évalué à 9.
Bon je suppose que c'est juste un bel appeau à troll (on est trolldi après tout). Mais bon, j'en ai un peu marre des gens qui disent que Perl c'est pas lisible.
Oui, il y a quelques symboles particuliers à connaître en Perl, en particulier
$_
(variable utilisée par défaut dans lors de l'itération sur une collection ou lors de la lecture d'un fichier),@_
(tableau des paramètres passés à une fonction),$!
(errno), $(résultat d'une évaluation avec les backticks), $@ (erreur au retour de l'appel à
eval) et pour certains cas très particuliers lors de traitements d'entrées/sorties
$/` (séparateur de ligne, "\n" par défaut), et $| (E/S bufferisées ou pas). Il y en a plein d'autres. Mais perso je ne les utilise pas, et je les vois rarement employés.Pire: en tant qu'utilisateur de bibliothèques/modules Perl, les modules eux-même font souvent usage de plein de symboles, mais le code utilisateur lui se conte de bêtes
my $instance = MON::MODULE->new(...); $instance->ma_methode(...);
Bref, laissez mon Perl tranquille !
[^] # Re: euh?!
Posté par Maclag . Évalué à 10.
Pour Perl, comme pour J à vue de nez, je ne dirais pas que le code est lisible ou pas lisible. Je pense que comme tu dis, ça dépend beaucoup de qui code.
Mais par contre, je dirais que là où par exemple Python oblige quasiment à coder très lisiblement, Perl offre la possibilité de réaliser parmi les pires délires d'obfuscation jamais imaginées, et plus généralement ne met que peu de contraintes au développeur sur la lisibilité du code.
C'est un peu comme dire que Java c'est lent et lourd. C'est surtout que Java c'est "facile à apprendre" et très populaire. Un débutant peut rapidement faire un programme lent et lourd en Java, et décider qu'il n'est plus débutant vu que "ça marche".
[^] # Re: euh?!
Posté par Philippe F (site web personnel) . Évalué à 1.
Si tu mets un programmeur qui ne connait pas Python devant un programme Python, il arrive en général à le comprendre (quoique les "list-comprehensions" ont un peu relevé le niveau). Tu n'a jamais cet effet là devant un programme Perl.
Dans tout langage, tu peux écrire très lisiblement et de façon obtuse. Après, il y a la moyenne et le style encouragé par le langage. Perl clairement encourage un style cryptique pour qui ne parle pas le Perl. La même chose peut être dit du LISP, mais certainement pas du PHP, Java ou C#.
[^] # Re: euh?!
Posté par vermillon . Évalué à 8.
Je m'avance peut-être un peu, mais il me semble que le point commun entre Python, Java, PHP et C#, c'est une "relative" (j'insiste sur le relative..) similarité de la syntaxe, surtout quand on les oppose à Perl/LISP. Du coup, c'est un peu normal qu'ils apparaissent plus lisible pour un novice.
Quand tu dis "un programmeur qui ne connait pas Python", quelles suppositions fais-tu sur les langages qu'il connaît?
[^] # Re: euh?!
Posté par spider-mario . Évalué à 4.
Il ne me semble pas que Perl encourage quoi que ce soit de cryptique. Aurais-tu des sources ?
J’en ai une où Larry Wall dit au contraire qu’on utilise Perl comme on le veut : You Can Cuss in Perl.
[^] # Re: euh?!
Posté par Sam E. (site web personnel) . Évalué à 6.
T'as des sources sur ce que t'avances ?
J'ai en tout cas deux remarques à faire.
La premières est que les dialectes Lisp (en tout cas pour Clojure et Emacs Lisp, c'est les seuls que j'étudie et pratique) ont une syntaxe extrêmement simple et encouragent clairement à coder de façon explicite. Je vois pas du tout pourquoi tu fais le rapprochement avec le perl, y a pas vraiment de rapport.
Pour la deuxième : T'es sérieux ? Tu considères réellement que PHP encourage un style propre ? Parce qu'à chaque fois que je dois en faire et que je lis la documentation officielle, je me fais la réflexion inverse.
Étant donné les langages que tu mets dans la seconde catégorie (qui ont tous une syntaxe de base assez proche, découlant du C) je pense surtout qu'il s'agit surtout d'une question d'expérience. Si effectivement tu n'as utilisé que des langages découlant du C le reste doit te sembler ésotérique. Joue un peu avec, tu verras que ça fait pas aussi peur qu'il n'y paraît.
Et sinon, parce que je trouve sympa comme la chose est analysée, j'aime bien pointer vers cet article quand les gens débattent de la lisibilité du Perl :)
[^] # Re: euh?!
Posté par neil . Évalué à 5. Dernière modification le 23 août 2013 à 17:57.
Dans les autres langages ça ressemble plus à un fold.
Pas du tout. C’est perdre tout l’avantage du point free, l’un des principaux apports de la programmation fonctionnelle, qui permet de réfléchir sur les fonctions plutôt que sur les données (voir la liste des avantages sur Wikipédia ou le HaskellWiki). D’autant qu’il s’agit d’un des points forts de J, inspiré des travaux de Backus.
[^] # Re: euh?!
Posté par Nicolas Boulay (site web personnel) . Évalué à 0.
"It helps the writer (and reader) think about composing functions (high level), rather than shuffling data (low level). "
Le codeur moyen en est parfaitement incapable.
f x = x + 1
==
f = (+ 1)
Franchement, cela rajoute quoi comme sémantique ? C'est exactement la même chose.
"La première sécurité est la liberté"
[^] # Re: euh?!
Posté par Zylabon . Évalué à 5.
Il ne s'agit pas vraiment de composition de fonction mais d'adopter un style pointless, c'est un peu plus fort.
Là oui, mais pour cet exemple :
Certes, la sémantique est la même concernant les données, mais pas l'implémentation. La première implémentation
f
est bien plus lente que la secondef'
. Dansf
le compilateur voit que la valeur de g dépend de x, et donc va créer la fermeture à l’exécution (à chaque appel). Dansf'
, on donne la "vrai" définition def'
, c'est "la fonction qui prend une liste d'entiers et retourne la liste où chaque entier est incrémenté". Un compilateur malin va pouvoir "inliner" le(+1)
dans lemap
et compiler du code optimal (dans ce cas simple).Please do not feed the trolls
[^] # Re: euh?!
Posté par anaseto . Évalué à 2.
Dans le même ordre d'idée, en J certaines phrases idiomatiques dans un style tacite peuvent être optimisées par du code spécial.
Par exemple avec l'adverbe
\
(« préfixe ») qui applique un verbe aux préfixes d'un tableau :On pourrait penser que
+/\
est en temps quadratique, mais le compilateur reconnaît cette composition classique du verbe + et des deux adverbes/
et\
et produit du code efficace:et ça en 0.14s sur ma machine à 1.5Ghz.
[^] # Re: euh?!
Posté par anaseto . Évalué à 2.
Heu, le x: de mon calcul était stupide : passer en précision arbitraire après donne un résultat faux pour les derniers chiffres logiquement ;), donc c'est juste un flottant avec des chiffres faux vers la fin.
[^] # Re: euh?!
Posté par Nicolas Boulay (site web personnel) . Évalué à 1.
Encore une fois, ces bidouilles de langage sont là pour palier des défaut du compilateur. Reconnaitre l'usage de certaine forme d'opération et les optimiser, c'est la base d'un compilateur optimisant. Gcc va jusqu'à réécrire les nids de boucles pour les rendre plus régulière pour les vectoriser.
"La première sécurité est la liberté"
[^] # Re: euh?!
Posté par anaseto . Évalué à 4.
Ce que je voulais dire c'est qu'a priori il est plus simple pour un compilateur de faire des manipulations d'optimisation sur un code « algébrique », et reconnaître une combinaison de symboles plutôt que des structures complexes.
Et je ne vois pas trop ce que tu veux dire avec ta première phrase : la combinaison d'adverbes
/\
peut s'appliquer à n'importe quel verbe, et l'idée c'est de permettre son utilisation dans les cas complexes tout en ne perdant pas en vitesse dans les cas simples facilement optimisables, c'est donc une bidouille utile et facile à implémenter.[^] # Re: euh?!
Posté par Nicolas Boulay (site web personnel) . Évalué à 1.
Vu que sémantiquement, c'est la même chose, le problème vient du compilateur. Rendre le code peut lisible, n'est jamais une bonne solution.
"La première sécurité est la liberté"
[^] # Re: euh?!
Posté par anaseto . Évalué à 4. Dernière modification le 25 août 2013 à 12:09.
Je crois qu'on s'égare un peu. Ce que disait Zylabon était que certains codes étaient plus faciles à optimiser (ou du moins c'est ce que j'ai compris).
Quant à la lisibilité du code, il faut juste je crois accepter que tout le monde n'en a pas la même notion, tout le monde ne travaille pas de la même façon et n'a pas les mêmes besoins, ce qui compte c'est d'être à l'aise avec son code et celui des gens avec qui on travaille, mais si d'autres travaillent ailleurs différemment, tant mieux pour la diversité. Personne n'est obligé de comprendre le code de tout le monde heureusement.
[^] # Re: euh?!
Posté par Zylabon . Évalué à 3.
Je me répond à moi même pour me dire que mon exemple était particulièrement stupide, celui ci est bien mieux :
Please do not feed the trolls
# Commentaire supprimé
Posté par Anonyme . Évalué à 10.
Ce commentaire a été supprimé par l’équipe de modération.
# Licences libres donc commerciales
Posté par thamieu . Évalué à 3.
La GPL autorisant l'utilisation du logiciel pour tous les usages sans restrictions et sa diffusion (sans s'intéresser à qui diffuse ni qui reçoit), elle permet le commerce. Donc la caractéristique des autres licences possibles n'est pas d'être commerciales, mais d'être payantes ou d'accorder des droits différents aux utilisateurs.
[^] # Re: Licences libres donc commerciales
Posté par anaseto . Évalué à 3.
Oui, c'est que je voulais dire, que d'autres licences étaient apparemment négociables.
# PKGBUILD
Posté par spider-mario . Évalué à 1.
Pour ma part, j’ai fait un PKGBUILD pour Arch Linux. Pour éviter le conflit, je renomme
jconsole
enj
tout court. (Du coup, c’est pratique à entrer dans un terminal quand on veut s’amuser un peu. :p)[^] # Re: PKGBUILD
Posté par anaseto . Évalué à 2.
J'ai
jc
à la place dejconsole
sous Gentoo. Ton message me fait penser que j'ai oublié de dire qu'il y a un dépôt git openj avec quelques modifications par rapport à la version de mars 2011, c'est peut-être celle que tu utilises. L'ennui c'est que ça a l'air un peu arrêté, et il y a une petite régression du 3/02/2012 sur les modulos pour les nombres complexes (ou « feature », mais quelques résultats me semblaient louches quand même). En tout cas c'est pas vraiment documenté, et trois tests unitaires ne passent plus chez moi.Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.