Oui, un fichier pour chaque exemple, bien séparer. Pour pouvoir comparer les évolutions et les différences. Genre je suis bloqué sur le fold appliquée aux parenthèses, mais je bloque surtout sur le principe de précédence, plus que sur le code.
Je vais voir ce que je peux faire pour mettre du code plus détaillé et commenté sur github. Je ne te promets rien, cela dépendra de mon temps libre.
Pour le principe de précédence qu'est-ce qui te pose problème ? On a d'abord une fonction de mise entre parenthèse sous condition :
Elle permet de retourner une fonction de type string -> string selon la valeur du booléen : soit cette fonction met des parenthèses si b = true, sinon c'est l'identité et on ne met pas de parenthèses. Ensuite pour le cas de l'addition, on a :
letaddxy=funp->cparen(p>3)@@show_add(x3)(y4)
Autrement dit, tant que la priorité du contexte est ≤ 3 on ne met pas de parenthèse : 3 représente le degré de priorité de l'opérateur additif. Ensuite comme l'addition est associative à gauche, pour le membre de gauche on reste au degré 3 de l'addition, par contre pour le membre de droite on passe au degré suivant. C'est pour distinguer 1 + 2 + 3 == ((1 + 2) + 3) de 1 + (2 + 3) == (1 + (2 + 3)). Pour la multiplication, son degré de priorité est de 4 : la multiplication est prioritaire sur l'addition.
Cela étant, c'est plus un code « standard » pour gérer la priorité des opérateurs en programmation fonctionnelle. Si tu ne vois pas trop pourquoi il fait ce qu'on lui demande, ce n'est pas très important pour le reste du journal.
Mais pourquoi ?! En objet, c'est simplement une injection. Un composant d'un objet qui est construit à l’extérieur de celui-ci et donné en paramètre au constructeur. En quoi instancier un module en fixant un des type paramètre en fait une fonction ?
J'ai du mal à comprendre où tu veux en venir, et je me suis peut être mal exprimé. Les implémentations de la signature d'un langage corresponde aux différentes interprétations du dit langage. Les foncteurs eux servent à écrire du code dans ses langages (comme les foncteurs d'exemples) ou à transformer les dits programmes (comme les optimiseurs). Pour moi, et comme l'a dit Thomas Douillard, c'est exactement le niveau d'abstraction qu'il me faut pour raisonner sur ce genre de problématique.
Depuis que j'ai découvert cette méthode, et également suite aux discussions sur le journal de présentation de MetaOCaml, il y a deux comics xkcd qui résument bien mon état du moment :
nerd snipping
et celui-ci :
purity
J'ai essayé d'éviter le plus possible le recours au vocabulaire et aux concepts abstraits des mathématiques, mais ce n'est pas toujours évident.
Pour ce qui est de l'approche final avec les objets de OCaml cela pourrait ressemblait au code suivant :
(* on définit la symantique d'un langage avec litéraux * et addition comme un type d'objet paramétré par son interprétation *)classtype['repr]symAdd=objectmethodlit:int->'reprmethodadd:'repr->'reprend(* là on a des fonctions pour créer nos termes * du langage à partir de ses implémentations *)letlitn=funro->ro#litnletaddxy=funro->ro#add(xro)(yro)(* un terme d'exemple *)lett1()=add(lit1)(add(lit2)(lit3))(* on implémente deux interprétations *)classeval=objectmethodlitn=(n:int)methodaddxy=x+yendclassview=objectmethodlitn=string_of_intnmethodaddxy="("^x^" + "^y^")"end(* on instancie un objet pour chacune *)leteval=newevalletview=newview(* notre exemple est une fonction qui prend une * instance de classe, à la manière d'un foncteur *)t1()eval;;-:int=6t1()view;;-:string="(1 + (2 + 3))"
Et pour étendre les langages, par exemple en rajoutant la multiplication, on fait de l'héritage entre objets :
(* on ajoute un opérateur aux langages *)classtype['repr]symMul=objectmethodmul:'repr->'repr->'reprend(* la nouvelle fonction pour écrire nos termes *)letmulxy=funro->ro#mul(xro)(yro)(* on étend par héritage nos précédents interprètes *)classevalM=objectinheritevalmethodmulxy=x*yendclassviewM=objectinheritviewmethodmulxy="("^x^" * "^y^")"end(* on les instancie *)letevalM=newevalMletviewM=newviewM(* un exemple de test *)lett2()=mul(add(lit1)(lit2))(add(lit3)(lit4))t2()evalM;;-:int=21t2()viewM;;-:string="((1 + 2) * (3 + 4))"
Cette façon de faire est plus proche de l'encodage en Haskell via les typeclasses.
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: chaud
Posté par kantien . En réponse au journal Tagless-final ou l'art de l'interprétation modulaire.. Évalué à 2. Dernière modification le 23 novembre 2016 à 17:35.
Je vais voir ce que je peux faire pour mettre du code plus détaillé et commenté sur github. Je ne te promets rien, cela dépendra de mon temps libre.
Pour le principe de précédence qu'est-ce qui te pose problème ? On a d'abord une fonction de mise entre parenthèse sous condition :
Elle permet de retourner une fonction de type
string -> stringselon la valeur du booléen : soit cette fonction met des parenthèses sib = true, sinon c'est l'identité et on ne met pas de parenthèses. Ensuite pour le cas de l'addition, on a :Autrement dit, tant que la priorité du contexte est
≤ 3on ne met pas de parenthèse :3représente le degré de priorité de l'opérateur additif. Ensuite comme l'addition est associative à gauche, pour le membre de gauche on reste au degré 3 de l'addition, par contre pour le membre de droite on passe au degré suivant. C'est pour distinguer1 + 2 + 3 == ((1 + 2) + 3)de1 + (2 + 3) == (1 + (2 + 3)). Pour la multiplication, son degré de priorité est de4: la multiplication est prioritaire sur l'addition.Cela étant, c'est plus un code « standard » pour gérer la priorité des opérateurs en programmation fonctionnelle. Si tu ne vois pas trop pourquoi il fait ce qu'on lui demande, ce n'est pas très important pour le reste du journal.
J'ai du mal à comprendre où tu veux en venir, et je me suis peut être mal exprimé. Les implémentations de la signature d'un langage corresponde aux différentes interprétations du dit langage. Les foncteurs eux servent à écrire du code dans ses langages (comme les foncteurs d'exemples) ou à transformer les dits programmes (comme les optimiseurs). Pour moi, et comme l'a dit Thomas Douillard, c'est exactement le niveau d'abstraction qu'il me faut pour raisonner sur ce genre de problématique.
Depuis que j'ai découvert cette méthode, et également suite aux discussions sur le journal de présentation de MetaOCaml, il y a deux comics xkcd qui résument bien mon état du moment :
nerd snipping
et celui-ci :
purity
J'ai essayé d'éviter le plus possible le recours au vocabulaire et aux concepts abstraits des mathématiques, mais ce n'est pas toujours évident.
Pour ce qui est de l'approche final avec les objets de OCaml cela pourrait ressemblait au code suivant :
Et pour étendre les langages, par exemple en rajoutant la multiplication, on fait de l'héritage entre objets :
Cette façon de faire est plus proche de l'encodage en Haskell via les typeclasses.
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.