Acid, le lisp-like de la communauté !

Créons notre langage de programmation ! Pour le fun !

a marqué ce sujet comme résolu.

Salut les agrumes !

Vous avez été plutôt nombreux à vous présenter volontaire pour l'idée d'Emeric : Créer un petit langage ZdS. Et après de nombreuse discutions des groupes aux envies différentes se sont formés. Ce sujet est dédié à l'un de ces groupes : celui qui est intéressé par la réalisation d'un langage interprété inspiré de Lisp.

Provisoirement, le projet à été nommé Acid, en référence au goût des agrumes et parce que c'est le contraire (en chimie) du langage BASIC. Vous pouvez voter pour un nom ici.

Voici une sorte de FAQ à propos de l'organisation de ce développement.

Comment on s'y prend ?

Ayant personnellement l'envie de vraiment avancer dans ce projet, je préfererais que l'on suive une organisation par étape. Je demande donc à suivre cette organisation :

  1. Introduction : discussions autour de l'organisation et de la programmation de ce langage FAIT
  2. Spécification : Choix sur la syntaxe et les fonctionnalités du langage FAIT
  3. Implémentation : Chacun implémente Acid dans le langage qu'il veut, une organisation Github à été créée pour tout centraliser.

EDIT : Le système de développement, prévois finalement une organisation plus progressive, les spécifications se rajoutent au fur et à mesure de leurs implémentations.

Et moi ? Je fait quoi ?

Chaque membre peut participer comme il l'entend, il peut venir discuter des fonctionnalités ici, trouver des ressources intéressantes pour les développeurs, implémenter un interpréteur dans le langage qu'il veut, créer des modules… Faites de votre mieux et touchez à tout !

Et la "philosophie" du langage ?

Un point important pour avancer : Notre langage doit fournir uniquement les fonctions de bases (c'est déjà beaucoup), par là j'entend les blocs conditionnels, boucles, fonctions, modules.

EDIT : Il a été décidé afin d'augmenter la vitesse de développement, de centraliser une partie de toute les implémentations : La partie finale de l'interpréteur. Plus d'infos ici.

A vous les studios !

C'est désormais à nous de faire de ce petit projet quelque chose d'amusant qui puisse nous occuper durant les futures vacances d'été. S'il vous plait, évitez de débattre pour des petites choses (utilisez le +1/-1 pour voter les propositions peu importantes, par exemple).

Liens

Voilà voilà, en espérant que l'on fasse quelque chose de bien ! Mais que l'on s'amuse surtout ! :)

+6 -0

Je cite ici le message de Dominus, car c'est une base solide sur laquelle s'appuyer :

J'ai un peu réfléchi à la question, et voici quelques idées et suggestions pour la #TeamFonctionnel. Vous êtes évidemment libres de les accepter ou non, de les compléter, de faire ce que vous voulez…

Les fonctionnalités du langage

Les lambdas

À mon avis, le cœur d'un langage essentiellement fonctionnel, et donc la première chose à définir, ce sont les lambdas, ou fonctions anonymes. Une lambda serait définie par le mot-clé lambda, une liste d'arguments, et un corps de fonction. Par exemple, une lambda qui additionne les carrés de deux nombres prendrait la forme suivante.

1
2
3
(lambda (x y) (
    (+ (* x x) (* y y))
))

Pourquoi définir des lambdas plutôt que des fonctions ? Pour deux raisons. Premièrement, parce qu'un langage fonctionnel puissant se doit de pouvoir passer une fonction en argument à une autre fonction, et renvoyer des fonctions : ces fonctions n'ont pas nécessairement besoin d'être nommées, et il est plus simple d'avoir un mot-clé pour définir une lambda, et un autre pour donner un nom à un objet, qu'un mot-clé pour les fonctions avec un nom, et un autre pour les fonctions anonymes.

Deuxièmement, parce que par leur seul présence, on donne la possibilité d'utiliser des fonctions curryfiées (c'est-à-dire ayant déjà reçu une partie de leurs arguments). Par exemple, la fonction curryfiée (-1) de Haskell se traduirait en zLang par ce code.

1
(lambda x (- x 1))

Le nommage

Ce n'est pas tout de dire qu'on va avoir des fonctions nommées, encore faut-il avoir le mot-clé pour le faire. Celui-ci est tout trouvé : define, suivi d'un nom, puis d'un objet à nommer (qui pour l'instant, ne peut être qu'une lambda).

1
2
3
(define carrédelhypothénuse (lambda (x y) (
    (+ (* x x) (* y y))
)))

Les types natifs

Pour un certain nombre de types, il est plus facile de les définir nativement. À priori, les types natifs suivants suffisent :

  • Int ;
  • Int8, Word8, Int16, etc. ;
  • Float et Double (peut-être même juste le second) ;
  • Char (qui représente un caractère Unicode, par un entier sur 8 bits) ;
  • tuple … (par exemple, (tuple Int Char Word64)).

Comme on le verra un peu plus loin, on peut se passer de définir des booléens nativement, on pourra le faire dans la bibliothèque standard. Idem pour les chaînes de caractères, qui seront définis à partir du type liste chaînée qu'on définira dans la bibliothèque standard.

Les types algébriques

Pour ceux qui ne seraient pas familiers de la programmation fonctionnelle, les types algébriques sont un moyen de définir n'importe quel type très simplement par une combinaison de deux procédés :

  • les types produits, qui ne sont rien d'autre que des tuples nommés, par exemple Point Double Double (c'est du Haskell, comprendre, un Point est la réunion de deux Double) ;
  • les types sommes, qui permettent à un type de prendre plusieurs formes (on dit qu'il a plusieurs constructeurs). D'où l'exemple suivant, toujours en Haskell.
1
data Bool = False | True

Il faut comprendre, un Bool peut-être soit un False, soit un True. Et là où cela devient vraiment puissant, c'est qu'on peut combiner les deux. Voici comment une liste chaînée est usuellement définie.

1
data List a = Nil | Cons a (List a)

En français : une liste de a (List est un type paramétré, on peut mettre n'importe quoi dedans) est, soit une liste vide appelée Nil, soit un Cons, qui est la réunion d'un a et d'une liste de a.

Dans ma première proposition, je suggérais de laisser de côté les types paramétrés dans un premier temps. Je retire cette suggestion, parce que trois des types les plus utiles de la programmation fonctionnelle (les listes, les options (Maybe en Haskell, Option en Rust) et les alternatives (Either en Haskell, Result en Rust)) sont tous paramétrés. On perdrait beaucoup à ne pas les avoir.

Alors voici ma proposition. Pour définir un type, on doit définir un ou plusieurs constructeurs, prenant des types en paramètres. Le type lui-même peut prendre des paramètres, qui peuvent ensuite être utilisés dans les constructeurs. Voici le type Option du Rust, tel qu'il serait défini en zLang.

1
2
3
4
(define Option (type a (
    None
    (Some a)
)))

Le filtrage par motifs

Pas de grande difficulté là-dedans, une commande match, qui prend une expression, et une série de combinaisons valeur / motif + corps de fonction. Par exemple, une fonction qui fait la somme des éléments d'une liste.

1
2
3
4
5
6
(define somme (lambda liste (
    match liste (
        (Nil 0)
        ((Cons val suite) (+ val (somme suite)))
    )
)))

C'est pour cette raison qu'on peut se contenter de définir dans la bibliothèque standard un type Bool plutôt que d'en faire un type natif. Ce qui en Haskell s'exprimerait comme if x < 3 then x + 2 else x - 6 peut s'exprimer en zLang comme suit.

1
2
3
4
match (< x 3) (
    (True (+ x 2))
    (False (- x 6))
)

Une question qui reste posée est la suivante : faut-il qualifier les constructeurs ? C'est-à-dire que, quand on l'utilise en dehors de sa définition même, doit-on écrire Bool::True (syntaxe de Rust) ou simplement True (syntaxe de Haskell) ? Dans la plupart des cas, la première solution est la plus pratique : ça permet d'avoir plusieurs types qui ont un constructeur portant le même nom, comme None. Mais pour quelques types (comme les booléens), c'est vraiment plus simple de donner le constructeur directement.

Quatre solutions possibles.

  1. Les constructeurs sont toujours qualifiés.
  2. Les constructeurs ne sont jamais qualifiés (Haskell).
  3. Les constructeurs sont qualifiés par défaut et un mot-clé supplémentaire permet de déqualifier les constructeurs d'un type donné.
  4. Les constructeurs sont qualifiés par défaut mais le mot-clé permettant d'importer dans l'espace de nom courant le contenu d'un module permet d'importer les constructeurs d'un type donné, traité comme s'il était un module (Rust).

Je préfère la dernière solution, même si le mot-clé en question ne deviendra vraiment utile que si l'on finit par ajouter un système de modules.

Les contraintes de types

Comment dire qu'une fonction doit avoir un type donné ? Ou qu'une expression donnée au sein d'une expression plus vaste doit avoir un type donné ? À l'aide du mot-clé hastype, qui s'utilisera de deux façons différentes, comme par exemple, ce qui suit.

1
2
3
(hastype (Double -> Double -> Double) carrédelhypothénuse)

(lambda x (+ x (hastype Word8 42)))

Le premier est pour les éléments qui ont un nom, le second pour les éléments anonymes.

Les entrées-sorties

Si on reste très basiques, on peut se contenter de deux fonctions, getchar et putchar. La principale difficulté, c'est quel type donner à ces fonctions ? Comment faire pour exécuter plusieurs de ces instructions à la suite, comme dans un langage impératif. Il existe une multitude de solutions possibles, n'impliquant pas nécessairement une monade. Je vous laisse y réfléchir. :)

Un peu de méta

Bon, c'est cool le filtrage par motif, mais la syntaxe utilisée pour une simple condition n'est vraiment pas intuitive, on préférerait avoir if condition cas_true cas_false. Eh bien, c'est tout à fait possible. On pourrait définir ceci dans la bibliothèque standard.

1
2
3
4
5
6
7
8
(hastype (Bool -> a -> a -> a) if)

(define if (lambda (cond cas_true cas_false) (
    match cond (
        (True cas_true)
        (False cas_false)
    )
)))

Mais ce n'est pas toujours possible ainsi. Comment définir un mot-clé function qui prend un nom, une liste d'arguments et un corps de fonction, et qui en fait une combinaison de define et de lambda ? Je n'ai pas encore trouvé de solution satisfaisante, alors je vous invite à y réfléchir.

Des commentaires

Le système du C/C++ avec // et /* */ me paraît très bien.

Ce que doit faire l'interpréteur / compilateur

  • Vérifier qu'il n'y a aucune erreur de syntaxe pure (manque une parenthèse, etc.).
  • Vérifier que tous les noms utilisés ont été définis à un endroit qui les rend visibles à l'endroit où ils sont utilisés.
  • Vérifier que toutes les fonctions définies au niveau du programme ont une déclaration de type quelque part.
  • Vérifier que les déclarations de type sont cohérente entre elles.
  • Interpréter / compiler.

Dominus Carnufex

+7 -0

J'ai pas trop suivi tout le débat sur le sujet d'Emeric, mais je me dis que c'est pas plus mal que tu prennes l'initiative de commencer.

Par contre, un point que je ne comprends pas bien, tu dis que l'objectif est de créer un interpréteur d'un langage Lisp-like en Python, et qu'il doit pouvoir lire et utiliser des modules écrit en Python ? Le but n'est-il pas justement d'écrire entièrement les modules dans le langage que l'on crée ? Même si tu évoques cette idée, je pense que cela serait bien plus intéressant de uniquement partager les modules sous cette forme, car ça permet d'utiliser le langage en lui même.

S'il vous plait, évitez de débattre pour des petites choses (utilisez le +1/-1 pour voter les propositions, par exemple).

Personnellement, j'ai plutôt l'impression que les +1/-1 cassent un peu le débat en apportant aucuns nouveaux arguments alors qu'il est souvent possible d'en imaginer. Il est plus intéressant de répondre à la personne (que ce soit positif ou négatif) en amenant différents exemples et arguments pour soutenir son idée.

Sinon sans aucun rapport :

développer

Juste, je pense que les modules devrait le plus possible être codée en zlang/Acid/Clementine/Mandarine/0b8eac puisqu'ainsi si un membre décide de faire sa version de l'interpréteur/compilateur en un autre langage que python il n’aura pas à coder un interpréteur python.

Une autre bonne idée serait de transformé les modules en bytes code interprétable pas une machine virtuelle, et je doit avouez que je trouve ma 2nd proposition plutôt cool ^^

Edit: napnac m'a un peu précédé :)

+2 -0

C'est un bon argument, mais l'avantage d'utiliser Python serait d'ajouter une compatibilité pour deux langages (Python et C). De plus, rien n'interdit d'utiliser Acid pour créer ses modules, c'est même encouragé !

Ici, nous coderons Acid en Python, mais si un membre décide d'implémenter Acid dans un autre langage trois choix s'offre à lui :

  • Ne pas supporter les modules Python/C
  • Intégrer l'interpréteur Python à son implémentation
  • Supporter les modules dans le langage dans lequel est implémenté Acid

Je trouve que c'est toujours mieux que rien. Pour l'idée de convertir les modules en bytecode est-ce que c'est vraiment utile pour ce qu'est le projet actuellement ?

Toutefois, je vais modifier le passage sur les modules pour montrer qu'il peut être intéressant de coder les modules directement en Acid.

Personnellement, j'ai plutôt l'impression que les +1/-1 cassent un peu le débat en apportant aucuns nouveaux arguments alors qu'il est souvent possible d'en imaginer. Il est plus intéressant de répondre à la personne (que ce soit positif ou négatif) en amenant différents exemples et arguments pour soutenir son idée.

Je voulait plutôt dire d'utiliser les +1/-1 pour voter certaines décisions peu importante. (éviter de débattre pendant 8 pages pour savoir si on met une virgule ou non par exemple…)

Ce que je souhaite c'est d'avancer non pas vite mais bien, et pas trop lentement non plus. :p

EDIT : fautes corrigées pour "développer"

+0 -0

S'il y a possibilité d'étendre le langage directement en Acid, c'est même mieux. Mais pour étendre le langage encore plus que ce que ses capacités natives peuvent faire, c'est très bien de faire des modules en Python ou en C (pour ma part, je me ferai un plaisir de faire des modules en C++ ;) ).

+2 -0

Ici, nous coderons Acid en Python

the_new_sky

Ah je croyais qu'on coderait juste Acid sur ce post, car je ne pense pas que tout le monde souhaite coder ce petit langage en Python (et c'est dommage de se restreindre de ce côté là). Encore une fois, je ne vois pas l'intérêt de coder les modules en Python ou en C, vu que le but est justement de manipuler le langage qu'on vient de créer. D'autant plus que si on est capable de faire un interpréteur, y a-t-il un réel challenge à coder des modules basiques ? Autant le faire avec un nouveau langage non ? :)

On a choisi Python, parce qu'il fallait bien choisir un langage, et que Python est très utilisé parmi les agrumes. Pour ma part, par exemple, je préfère le C++, mais je suis en train de consolider mes connaissances en Python pour participer :)

Et, pour répondre à ta question sur l'intérêt de tels modules, je vais me citer :

S'il y a possibilité d'étendre le langage directement en Acid, c'est même mieux. Mais pour étendre le langage encore plus que ce que ses capacités natives peuvent faire, c'est très bien de faire des modules en Python ou en C (pour ma part, je me ferai un plaisir de faire des modules en C++ ;) ).

mehdidou99

+4 -0

Ah je croyais qu'on coderait juste Acid sur ce post, car je ne pense pas que tout le monde souhaite coder ce petit langage en Python (et c'est dommage de se restreindre de ce côté là).

napnac

Le problème c'est que si on code dans des langages différents nous allons nous égarer et nous n'allons pas travailler à plusieurs, c'est dommage… Ne vaut-t-il pas mieux s'unir et tous contribuer ensemble ?

Encore une fois, je ne vois pas l'intérêt de coder les modules en Python ou en C, vu que le but est justement de manipuler le langage qu'on vient de créer. D'autant plus que si on est capable de faire un interpréteur, y a-t-il un réel challenge à coder des modules basiques ? Autant le faire avec un nouveau langage non ? :)

napnac

Je t'invite à relire le premier message (que je viens d'éditer) pour avoir ma position sur le sujet. Permettre les modules en Python (donc aussi en C) c'est étendre les possibilités du langage de la manière la plus simple possible. Bien sur pour les modules basique, codons-les en Acid :)

+2 -0

@mehdidou99 : J'avais rédigé mon message avant donc je n'avais pas pris en compte ta réponse. Pour moi ça serait plus l'interpréteur qu'il faudrait étendre au lieu de vouloir faire des modules dans un autre langage car celui qu'on développe n'est pas assez puissant (ce qui est peu élégant selon moi).

Pour ce qui est du choix du langage,

Le problème c'est que si on code dans des langages différents nous allons nous égarer et nous n'allons pas travailler à plusieurs, c'est dommage… Ne vaut-t-il pas mieux s'unir et tous contribuer ensemble ?

Je ne savais même pas qu'on devait développer le langage à plusieurs, pour moi on se mettait d'accord sur un langage et chacun proposait son implémentation (ce qui est plus logique pour un atelier). Après, j'aime bien l'idée de collaboration, mais je verrais plus cela s'appliquer aux modules qu'à l'interpréteur. ;)

Nous sommes bien d'accord, les modules ce sont les bibliothèques, pas vraiment des extensions à l'interpréteur ?

Sinon l'idée est vraiment de TOUT faire ensemble, tout le monde y trouverai son compte, les débutants pourrait apprendre certain concept sans tout coder, le projet est quand même assez gros, on est pas dans le cas d'un interpréteur Brainfuck. :p

Voilà mon avis, et ce que je compte faire ici… (avec vous ?)

+0 -0

Nous sommes bien d'accord, les modules ce sont les bibliothèques, pas vraiment des extensions à l'interpréteur ?

Un peu ambigu ta question, mais oui effectivement on pense bien à la même chose.

Sinon l'idée est vraiment de TOUT faire ensemble, tout le monde y trouverai son compte, les débutants pourrait apprendre certain concept sans tout coder, le projet est quand même assez gros, on est pas dans le cas d'un interpréteur Brainfuck. :p

Justement, je me demande si on est pas trop pour ce genre de projet (qui n'est pas énorme non plus). Faire des "équipes" seraient peut être plus approprié, car 2 ou 3 personnes suffisent largement, et au dessus ça risque d'être inutile pour les débutants qui n'y contribueront plus du tout.

Je viens apporter ma voix pour dire que les modules ne devraient pas pouvoir être codés en Python. C'est tout l'intérêt de la phase de spécification : trouver la bonne combinaison de concepts pour que le langage soit autosuffisant et que toutes les fonctionnalités supplémentaires soient écrites dans le langage lui-même.

Si vraiment vous voulez pouvoir coder des modules en tout ou partie dans un autre langage, je serais nettement plus favorable à l'ajout (plus tard !) d'un système de FFI, beaucoup plus élégant.

Sinon, je rejoins napnac. Je pense qu'il peut être intéressant de coder l'interpréteur / compilateur dans le langage que l'on veut, quitte à ce que ceux qui sont intéressés par un même langage d'implémentation travaillent ensemble, et de travailler tous ensemble à la spec du langage, et à la spec et au code de la bibliothèque standard. Mais j'y suis moins attaché qu'au point précédent. :-)

+3 -0

Honnêtement, je pense que c'est vraiment plus intéressant de se mettre au développement du langage ensemble, à plusieurs nous pourrons implémenter plus de fonctionnalité et rendre le projet plus gros. En réduisant le projet à des équipes de 2 ou 3, les débutants ne pourrons pas participer à leurs aise, alors que tous ensemble il leurs sera plus facile de travailler sur de petites parties.

+0 -0

Honnêtement, je pense que c'est vraiment plus intéressant de se mettre au développement du langage ensemble, à plusieurs nous pourrons implémenter plus de fonctionnalité et rendre le projet plus gros. En réduisant le projet à des équipes de 2 ou 3, les débutants ne pourrons pas participer à leurs aise, alors que tous ensemble il leurs sera plus facile de travailler sur de petites parties.

the_new_sky

Au contraire, si un débutant souhaite s'engager dans un futur gros projet avec 4-5 (ou plus) autres personnes bien plus expérimentées, il est peu probable qu'il contribue au langage car c'est facile d'être intimidé soit par le projet en lui même, soit par les autres développeurs plus compétents. Et honnêtement, pour avoir fait un projet très similaire récemment (http://buildyourownlisp.com/), il est assez facile d'avoir un petit langage sans trop de fonctionnalité, mais qui apportera bien plus à un débutant que quelques lignes de code très basique qu'il a écrit dans un fichier de plusieurs centaines de ligne rédigées par d'autres personnes.

Dans ce cas OK, je propose de partir là dessus, je vais créer une organisation Github afin de réunir tout les codes. Nous pourrons donc passer aux specs prochainement (attendre un peu l'arrivé d'autre personne sur le sujet).

Les specs permettrons de déterminer ou non comment supporter d'autre langage. Voilà qui semble résoudre temporairement ce débat XD

+0 -0

Dans ce cas OK, je propose de partir là dessus, je vais créer une organisation Github afin de réunir tout les codes. Nous pourrons donc passer aux specs prochainement (attendre un peu l'arrivé d'autre personne sur le sujet).

Pas obliger de tout centraliser :p Tu peux juste te charger de créer le repos principal où il y aura toutes les spécifications sur le langage (si le forum ne suffit pas déjà), et pour le reste, chaque candidat fait son propre repos de son côté ou crée un groupe si plusieurs personnes veulent travailler ensemble.

Les specs permettrons de déterminer ou non comment supporter d'autre langage. Voilà qui semble résoudre temporairement ce débat XD

Le but du débat n'est pas de le fermer le plus rapidement possible, mais d'y voir les points de vue de chacun pour choisir les plus intéressants et les plus satisfaisants (même si c'est vrai que les débats sur cet atelier semblent s'éterniser, mais pour le moment je n'ai pas l'impression qu'on tourne en rond).

Le repo principal existe déjà, il suffit de m'envoyer une pull request pour ajouter quelque chose.

Le but du débat n'est pas de le fermer le plus rapidement possible, mais d'y voir les points de vue de chacun pour choisir les plus intéressants et les plus satisfaisants (même si c'est vrai que les débats sur cet atelier semblent s'éterniser, mais pour le moment je n'ai pas l'impression qu'on tourne en rond).

napnac

Je suis d'accord mais je croit qu'il nous faut plus de personne pour discuter. C'est vrai que mon langage laisse penser que je veut fermer tout débat rapidement, mais ce n'est pas ce que je voulait dire. :) Nous somme 4, "2 contre 2" (je caricature) et nous avons de bons arguments chacun mais l'on va vite tourner en rond à ce rythme, attendons la venus d'autres agrumes pour mieux discuter. (mais il est vrai que rien n’empêche de continuer)

+0 -0

Yoyoyo !

C'est bon j'ai fait le deuil du choix de nom :'D

Tu parlais de philosophie dans la présentation, c'est quoi la philosophie du projet (et donc du langage) ? Il me semble que c'est de permettre à tout le monde de créer un langage et notamment les débutants. Est-ce qu'il est imaginable de donner une philosophie à notre langage ? Notamment au niveau de la syntaxe, d'avoir quelque chose d'intuitif (ie : plus dans les standard impératif que fonctionnel, de mon avis perso). D'ailleurs tu le montres bien dans ton poste Dominus, avec le if

En fait, on est 3 contre 1, je suis plutôt d'accord avec les deux autres. :p

J'imaginais plus l'interfaçage avec d'autres langages comme un système de FFI.

De plus, je pense que c'est une très bonne idée de lancer des implémentations dans différents langages, et rien n'empêche de participer à plusieurs implémentations en même temps. Pour ma part, je vais lancer une implémentation en C++ (qui m'aime me suive :) ) et participer à celle en python.

+3 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte