C'est comme pour n'importe quel outil. La responsabilité va sur le propriétaire sauf si l’outil a un défaut de conception, comme pour les voitures actuelles…
Le problème ne se pose même pas. Personne n’achèterait une voiture qui conduit comme ça.
Personne n'achèterait une voiture pour laquelle ils seraient considérés comme responsables en cas d'accident (surtout s'il cause la mort d'un tiers) alors qu'ils ne peuvent rien y faire. ;-)
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
S'il s'agissait de C j'aurais tendance à penser pareil, mais les map ou fold sur des listes deviennent des itérations avec range sur un tableau en Go […]
Ou alors, pour être plus générique sur la conception des itérateurs, utiliser des foncteurs applicatifs : The Essence of the Iterator Pattern.
Pour les types somme, je suis assez d'accord, mais c'est pas intrinsèquement fonctionnel comme truc, je dirais.
Effectivement, voir le chapitre 1 de ce cours de programmation avancée pour la comparaison entre Java et OCaml sur la manière de définir des types algébriques.
La seule construction que je ne vois pas comment réaliser à la fois efficacement et de façon commode dans un langage impératif un peu haut-niveau, c'est la récursivité terminale entre fonctions mutuellement récursives (comme truc approchant, je vois que le retour de pointeur vers une fonction, qui a le défaut de faire des sauts calculés non prévisibles).
Avec des goto ? :-P
Ok, ---> [] …
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
Pour le chiffre d'affaire qui semble t'inquiéter, le statut prévoit explicitement que s'il est de 42 et que la SOLE réussi à le décomposer en facteurs premiers (exploit inédit à ce jour) la SARL sera exonérée de charges ad vitam æternam pour service rendu à la patrie : cela évitera aux juges d'avoir à demander les clés de chiffrement.
Nul doute qu'à partir de l'excellence de la base de données dont dispose la SOLE, elle mènera a bien cet exploit. Il paraîtrait même qu'elle aurait déjà trouvé les facteurs 7 et 3 en s'écriant : « en ce septième jour de la semaine, dimanche de Pâques, louons la sainte trinité notre Père, son Fils et le Saint Esprit et célébrons la résurrection de notre Seigneur ». L'équipe en charge de la superviser redoute tout de même une possible dérive vers un délire mystique et cabalistique : une enquête est en cours.
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
Afin de lever ses doutes, la communauté scientifique a demandé à l'IA de se représenter elle-même :
Le verdict n'a pas fait une plie : nous avons bien affaire à une intelligence de tout premier ordre. Le risque est grand que l'être humain soit bientôt déclaré obsolète.
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
étrange, chez moi il n'y avait pas de raccourci défini…
Aller dans le menu de configuration du clavier, chercher « Masquer toutes les fenêtres normales » dans la section navigation, puis définir le raccourci à Super + D.
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
Concernant la récursivité en OCaml, y a-t-il une limitation du nombre d'appel récursif comme dans la plupart des langages impératifs ?
Absolument aucune limitation si les appels récursifs sont terminaux : il se font alors en espace constant sur la pile. Voir ma réponse du dessous à gndl.
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
C’est cette progressivité qui me manque souvent dans la documentation sur OCaml.
Tu parles de la documentation officielle ? Celle-ci est plus un manuel de référence du langage qu'une initiation aux principes de la programmation fonctionnelle.
La traduction de la boucle while que j'ai faite relève des principes généraux de la programmation fonctionnelle. C'est pour cela que j'avais donné un lien vers un de mes commentaires sur le journal de la version de taptempo en Emacs Lisp. Une personne cherchait à écrire la fonction factorielle de manière récursive terminale et ne savait pas comment faire. Il avait écrit la version naïve :
Dans les deux versions, impérative et fonctionnelle, la boucle dépend de l'entrée n. Mais, dans la version fonctionnelle, l'accumulateur est également un paramètre de la boucle, là où c'est une variable globale pour celle-ci dans le cas impératif.
Pour l'autre transformation du code, là c'est plutôt une astuce propre aux langages fonctionnels qui permettent d'avoir des opérateurs binaires infixes, donc hors famille Lisp et leur folie des parenthèses. L'idée étant que dans une telle situation :
step1x;step2x;step3x
on puisse « factoriser » la variable sur laquelle on effectue notre séquence de transformation :
(step1&step2&step3)x(* ou en chaînant à la manière d'une pipeline *)x|-step1|-step2|-step3
Ici, il faut pouvoir définir les opérateurs d'ordre supérieur & et |-, ce que peut faire n'importe quel langage fonctionnel, mais leur utilité réside essentiellement dans le fait qu'on les utilise de manière infixe, ce qui fournit du sucre syntaxique à cette approche.
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
Comme tu es plus habitué au paradigme impératif, je donne ci-dessous la boucle while équivalente à mon code avec une fonction récursive, puis je donne une explication pour passer de l'un à l'autre.
(* valeurs impératives modifiées pendant la boucle, on les définit à l'éxtérieur avant de rentrer dedans*)letsamples=Sample.createsample_sizeinletstamp=ref(Mtime_clock.now())inletkey_pressed=ref(tryinput_charstdinwithEnd_of_file->'q')in(* on rentre dans la boucle *)while!key_pressed<>'q'do(* on ne fait quelque chose qu'en cas d'appui sur 'enter' *)if!key_pressed='\n'thenbeginletnew_stamp=Mtime_clock.now()inletelapsed=Mtime.(Span.to_s(span!stampnew_stamp))in(* logique de mise à jour de la variable `samples` *)ifelapsed>reset_delaythenSample.resetsamples;Sample.addnew_stampsamples;(* on affiche le message adapté *)show_bpmsamples;(* mise à jour de la variable `stamp` *)stamp:=new_stamp;end;(* mise à jour de la variable `key_pressed` *)key_pressed:=tryinput_charstdinwithEnd_of_file->'q'done;
Le principe d'une boucle while, comme illustré dans cet exemple, est d'être une procédure qui modifie par effet de bords certaines variables qui lui sont globales (ici essentiellement samples et stamp) tant qu'une condition est satisfaite (ici, tant que l'on n'a pas appuyé sur q ou que l'entrée standard est n'est pas au bout). En tant que telle, du point de vue d'OCaml, c'est une expression qui a une valeur (comme n'importe quelle expression du langage) de type unit : ce type ne contient qu'une seule valeur (on parle de type singleton) notée (), c'est la valeur qui ne contient aucune information.
Pour transformer cette boucle en une fonction récursive, il faut d'abord qu'elle retourne la même valeur, à savoir (), lorsqu'elle termine. Ensuite, les variables globales sur laquelle opérait la boucle sont transformées en paramètres de la fonction : elles ne seront plus globales à la boucle, mais locales. On commence donc par écrire :
letrecloopstampsamples=
Ensuite, il faut exprimer la condition d'arrêt de la boucle. Celle-ci dépend du caractère que l'on lit sur l'entrée standard, d'où :
letrecloopstampsamples=matchinput_charstdinwith
On fait alors une étude de cas, à la manière d'un switch, en commençant par dire quand la fonction termine (et qui renvoie alors ()) :
letrecloopstampsamples=matchinput_charstdinwith(* cas de fin de boucle *)|exceptionEnd_of_file|'q'->()
Vient ensuite le cœur de la logique de la boucle, ce qui se passe quand on a appuyé sur enter :
letrecloopstampsamples=matchinput_charstdinwith(* cas de fin de boucle *)|exceptionEnd_of_file|'q'->()(* on a pressé 'enter' *)|'\n'->(* on remet la même logique qu'avec la boucle while *)letnew_stamp=Mtime_clock.now()inletelapsed=Mtime.(Span.to_s(span!stampnew_stamp))in(* logique de mise à jour de la variable `samples` *)ifelapsed>reset_delaythenSample.resetsamples;Sample.addnew_stampsamples;(* on affiche le message adapté *)show_bpmsamples;(* ici on ne met pas à jour la variable stamp mais on relance la boucle avec les nouveux paramètres *)loopnew_stampsamples
Il reste enfin à traiter le cas où l'on a appuyé sur une autre touche : on ne fait rien et on relance la boucle avec les mêmes paramètres
letrecloopstampsamples=matchinput_charstdinwith(* je ne réécris pas la gestion des autres cas *)(* cas par défaut : on boucle sans rien changer *)|_->loopstampsamples
Maintenant que le corps de la fonction est écrit, il ne reste plus qu'à l'appeler pour lancer la boucle. Pour ce faire, on appelle la fonction avec, pour paramètres, les valeurs initiales des variables globales de la boucle while :
letrecloopstampsamples=matchinput_charstdinwith(* bla bla bla bla*)inloop(Mtime_clock.now())(Sample.createsample_size)
Voilà le principe général pour transformer une boucle while en fonction récursive : on transforme les variables globales de la boucle en variables locales, et à chaque tour on lui passe les nouvelles valeurs.
Ceci étant, ce genre d'approche n'est pas propre au paradigme fonctionnel, mais ce dernier en fait un usage omniprésent et c'est la route vers la pureté (absence d'effets de bords)1. Pour l'instant, la variable samples fonctionne toujours pas effet de bords et, dans le message précédent, j'ai juste modifié l'écriture de sa logique de mise à jour en utilisant une approche par pipe avec le code :
Mais si, à la place d'une structure impérative, j'utilisais une structure purement applicative, j'aurais juste à changer les deux premiers combinateurs de tuyaux : un pipe |> au lieu d'un T |- et utiliser la fonction identity au lieu de ignore
samples|>(ifelapsed>reset_delaythenSample.resetelseidentity)|>Sample.addnew_stamp|-show_bpm(* ici on log donc on utilise toujours le tee *)|>loopnew_stamp
Derrière cette vision par pipeline, il y a une notion élémentaire de mathématique (bon c'est pas du niveau primaire, mais début de lycée ;-), à savoir la composition de fonction.
que l'on peut écrire (la pipeline est assez visible sur le diagramme) :
funx->x|>f|>g|>h
L'opérateur de composition est une opérateur d'ordre supérieur : il prend deux fonctions en entrée et en renvoie une en sortie; raison pour laquelle il a une place centrale de le paradigme de la programmation fonctionnelle. Que peux-t-on faire avec une fonction ?
les utiliser, ça on le fait dans tous les langages de la même façon ;
les composer, ça c'est plus simple à écrire dans un langage fonctionnel.
J'espère que ces explications t'aideront un peu mieux à comprendre certains principes élémentaires à la base du paradigme fonctionnel.
En réalité, c'est cette recherche d'absence d'effets de bord qui nous les fait écrire ainsi. On obtient alors des fonctions récursives avec appel dits terminaux, que le compilateur optimisera comme une simple boucle (il fera la traduction dans le sens inverse de celle que je viens de faire). Voir mon commentaire sur la version de taptempo en Emacs Lisp. ↩
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
Comme les compilateurs qui se respectent et qui se compilent eux-mêmes, n'importe quelle version d'upt pourra packager toutes les autres y compris elle-même. :-D
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
la réponse est dans le commentaire lié plus haut: stty -icanon désactive le buffering de STDIN, j'image qu'il est bufferisé pour des raisons de performance à l'origine
C'est le fonctionnement canonique (d'où le nom de l'option ;-) de l'entrée standard d'un terminal dans les systèmes Unix. man termios pour de plus amples informations (en désactivant la fonction echo, l'entrée standard ne renvoie pas ce qu'elle reçoit sur la sortie standard, par exemple).
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
Dans ce cas, je te propose la version suivante de la boucle principale (dans une approche similaire à celle en Elixir et ce que l'auteur à appeler flow programming).
Ici c'est dans la même veine que le code Rust que tu ne comprenais pas : c'est à base de pipe (on connecte les flux d'entrée et de sortie de fonctions). Il y a deux types de connecteurs de flux sous forme d'opérateurs infixes : |- qui est proche de la commande unix tee (d'où le choix du symbole) et le |> qui fonctionne comme le pipe| du shell.
Si tu comprends cette commande, alors c'est bon :
$ (for i in $(seq 6);do echo; sleep 0.5;done)| ./taptempo | tee log1 | grep bpm | tee log2 | cut -d' ' -f2,3
120 bpm
120 bpm
120 bpm
120 bpm
120 bpm
$ cat log1
Appuyer sur la touche entrée en cadence (q pour quitter).
[Appuyer encore sur la touche entrée pour lancer le calcul du tempo...]
Tempo: 120 bpm
Tempo: 120 bpm
Tempo: 120 bpm
Tempo: 120 bpm
Tempo: 120 bpm
Au revoir !
$ cat log2
Tempo: 120 bpm
Tempo: 120 bpm
Tempo: 120 bpm
Tempo: 120 bpm
Tempo: 120 bpm
on chaîne les commandes, et on utilise tee pour faire un log de résultats intermédiaires mais sans casser le flux. Comme dans cet exemple OCaml :
letpipelinestr=str|-Printf.printf"la chaîne est: \"%s\"\n"|>String.trim|-Printf.printf"on a retiré les espaces: \"%s\"\n"|>String.length|-Printf.printf"la longueur de la chaîne tronquée est: %i\n"|>succ|>(funx->3*x);;valpipeline:string->int=<fun>
on prend une chaîne, on lui retire les espaces en préfixe et suffixe (String.trim), on calcule la longueur de la chaîne ainsi obtenue (String.length) puis on effectue des calculs sur cette valeur entière, le tout en faisant quelques commandes de log entre temps. Exemple d'usage :
pipeline" hello world ! ";;lachaîneest:" hello world ! "onaretirélesespaces:"hello world !"lalongueurdelachaînetronquéeest:13-:int=42
L'opérateur infixe |- se définit tout simplement ainsi :
(* on applique f à x, puis on retourne x pour pouvoir chaîner *)let(|-)xf=(fx:unit);x;;val(|-):'a->('a->unit)->'a
j'ai juste mis une contrainte de type sur la sortie de f de telle sorte que :
x|-step1|-step2|-step3(* soit bien équivalent à *)step1x;step2x;step3x
Maintenant, si on reprend la partie du code qui correspond au traitement à effectuer lors de l'appui sur la touche enter :
On calcule un nouvelle horodatage et le temps écoulé (en secondes) depuis le dernier appui sur enter; ensuite on enchaîne les traitements sur la file des horodatages :
si le temps écoulé est supérieur au délai de réinitialisation alors on reset, sinon on ne fait rien ;
on ajoute l'horodatage à la file (ce qui met à jour, dans la structure, la valeur du tempo) ;
on affiche le tempo ou le message qui dit d'appuyer une nouvel fois pour lancer le calcul (choix basé sur un type option contenu dans la structure[1]) ;
on repart dans la boucle avec comme paramètres le nouvel horodatage et la file mise à jour.
C'est beau, c'est fin, ça se mange sans faim. :-)
[1]: en arrière plan, le type de la file d'horodatage est
typet={size:int;(* nombre max d'échantillons *)queue:Mtime.tQueue.t;(* FIFO contenant les timestamps *)mutabletempo:floatoption;(* Some tempo ou None *)}
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
Peux-tu m'expliquer comment le compiler et l'exécuter ?
Sans avoir à installer une partie des outils de développements OCaml, le plus simple est sans doute de passer par le gestionnaire de paquets de ta distribution. Sous Debian (ou dérivée), il te faudrait au minimum les paquets suivants :
Par contre, chez moi, l'internationalisation ne marche pas : j'ai le texte en anglais. De plus, le flux de sortie standard n'est pas vidé sur certaines écritures : je ne vois rien avant d'avoir appuyer plusieurs fois sur enter. Tu peux corriger cela en rajoutant flush stdout; après ces deux lignes de taptempocaml.ml :
print_string(s_"Hit enter key for each beat (q to quit).\n");flushstdout;(* <- à rajouter *)(* ... *)print_string(s_"[Hit enter key one more time to start bpm computation...]\n");flushstdout(* <- à rajouter mais pas besoin du `;' ici *)
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
Ce qui serait super cool maintenant, c'est que quelqu'un, pas forcément toi, mette une version plus OCaml comme on en voit traîner régulièrement sur LinuxFr :)
Qu'entends-tu par plus OCaml ? Tu veux dire dans un style fonctionnel ? Là son code est fondamentalement impératif (le langage est multi-paradigme) et les traits impératifs se prêtent bien au problème. Ceci étant on pourrait ne pas mettre de boucle while, mais une fonction récursive à la place, du genre :
c'est ce genre de code dont tu parles ? (ici j'utilise une horloge monotone et un module à ma sauce pour gérer la logique de calcul du tempo)
L'avantage que je vois, c'est que cela évite les imbrications, des fois dure à suivre, des if then else. Ici on est protégé des exceptions et seule la touche enter peut être appuyée en cadence. Sans cela, un test comme :
générera une exception (non rattrapée) quand le flux d'entrée est fermé et, selon que l'on accepte ou non n'importe quel caractère, cela peut changer le résultat de celui-ci :
# seule la touche enter
$ (for i in $(seq 6);doecho'taptempo'; sleep 0.5;done)| ./taptempo | grep bpm | wc -l
5# n'importe quel caractère
$ for i in $(seq 6);doecho'taptempo'; sleep 0.5;done)| ./taptempo | grep bpm | wc -l
53# sans compter le calcul du tempo qui s'envole ;-)
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
Les langages avec des vraies macros , comme Collin Lisp que je cite permettent de tout programmer de façon assez agréable. Par exemple avec cl-async, on peut presque réutiliser son code synchrone avec des promises, il suffit presque de changer les let en alet;)
Avec l'extension de syntaxe de Lwt aussi ;-).
Dans utop, il suffit de faire :
#require"lwt";;#require"lwt.ppx";;
Pour une version avec bind, proche de la syntaxe NodeJS, où on passe la promesse à une fonction anonyme :
letmain()=letopenLwtinLwt_io.print"Entrer votre nom: ">>=fun()->Lwt_io.(read_linestdin)>>=funnom->Lwt_io.printlf"Votre nom est: %s"nom;;valmain:unit->unitLwt.t=<fun>let()=Lwt_main.run(main());;Entrervotrenom:kantienVotrenomest:kantien
La même, mais avec l'extension de syntaxe : on retrouve une forme proche de celle d'un code synchrone (les let x = e in sont remplacés par des let%lwt x = e in) :
letmain()=let%lwt()=Lwt_io.print"Entrer votre nom: "inlet%lwtnom=Lwt_io.(read_linestdin)inLwt_io.printlf"Votre nom est: %s"nom;;valmain:unit->unitLwt.t=<fun>let()=Lwt_main.run(main());;Entrervotrenom:kantienVotrenomest:kantien
Tu penses (probablement) à une monade spécifique qui fait de la composition de fonctions.
Bah, c'est la monade identité, qui n'est pas spécialement la plus utile.
Par contre, je comprends pas trop où veut en venir Michaël : avec les monades et leur bind, on les utilise plutôt dans le sens inverse de la composition (à la manière du pipe du shell).
f g
A -----------> B -----------> C
| | |
| | |
V map f V map g V
F(A) -----------> F(B) -----------> F(C)
Là j'ai trois types A, B et C et deux fonctions f : A -> B et g : B -> C. Si je les compose, j'obtiens une fonction de A vers C. On peut le dire en mode direct (à la mode mathématique) : applique g au résultat de f; ou en mode inverse : passe le résultat de f à g. La conception à la manière d'une pipeline, c'est de le voire comme sur le dessin en mode inverse : on écrit les traitements dans l'ordre où ils sont effectués (f puis g).
Après une monade, c'est un type paramétrique (ici F sur le dessin) avec certaines bonnes propriétés. Tout d'abord on doit pouvoir faire un map dessus : à partir de f : A -> B, on peut construire map f : F(A) -> F(B) qui respecte la composition. Si je compose f et g, puis que j'applique map, ou que j'applique map puis que je compose, on doit obtenir le même résultat.
Ensuite, pour que ce type paramétrique soit une monade, il faut pouvoir combiner une fonction A -> F(B) avec un autre B -> F(C) : ce sont les diagonales que l'on cherche à assembler. Mais pour ce faire, on utilise plutôt l'opérateur bind (ou >>= en notation infixe) qui prend une valeur de type F(A), une fonction de type A -> F(B) et renvoie une valeur de type F(B). C'est l'équivalent du pipe pour les monades : le pipe est à la composition de fonctions, ce que le bind est à la composition d'opérateurs monadiques.
(* on va de A vers C en composant f et g *)x|>f|>g(* on va de F(A) vers F(C) via la monade f' et g' sont les fonctions en diagonales*)mx>>=f'>>=g'
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
D'ailleurs, je préfère la version verbeuse, je la trouve plus lisible ;)
C'est parce que la pipeline est courte. Il faut voir cette notation pointée de Rust comme le pipe | du shell : reader.next() | unwrap_or | map_err | map. Sur de longues pipelines, c'est plus simple à écrire et il vaut mieux laisser le compilateur inliner le tout plutôt que de le faire à la main (comme dans sa version verbeuse, qui contient d'ailleurs une erreur : le cas None doit retourner Ok(false) de type Result<Bool, String>).
En OCaml (j'étais bien obligé), on écrirait un code du genre :
Les patterns à base de map sont omniprésents dans le paradigme fonctionnel. Un type paramétrique à un paramètre (comme les options, les listes, les tableaux, les vecteurs…) est une fonction des types dans les types. Ainsi si j'ai une fonction qui transforme les objets du paramètre (disons des int en string), je peux la lifter pour opérer sur le type paramétrique (un peu comme une composition de fonction, si tu veux). Exemples :
(* un int optionnel devient un string optionnel *)Option.mapstring_of_int(Some1);;-:stringoption=Someu"1"(* une liste de ints devients une liste de strings *)List.mapstring_of_int[1;2;3];;-:stringlist=[u"1";u"2";u"3"](* un tableau de ints devient un tableau de strings *)Array.mapstring_of_int[|1;2;3|];;-:stringarray=[|u"1";u"2";u"3"|]
Le type Result est lui un type paramétrique à deux paramètres : on peut donc faire un map soit sur le type en paramètre de Ok, soit sur celui en paramètre de Err. Ce qui soit donne deux fonctions map (comme en Rust), soit une fonction map_both comme dans mon exemple en OCaml (ou bimap en Haskell) qui prend deux fonctions en paramètres (une pour chaque type).
(* la double map pour le type result *)letbimapfg=functionOkx->Ok(fx)|Errore->Error(ge)(* le unwrap_or comme en Rust *)letunwrap_ord=functionNone->d|Somex->x(* la pipeline comme en Rust *)letpipelinex=x|>unwrap_or(Ok"q")|>bimap(funs->s<>"q")(Printf.sprintf"Houla y'a eu un truc: %s")(* exemples de sortie *)pipelineNone;;-:(bool,string)result=Okfalsepipeline(Some(Ok"f"));;-:(bool,string)result=Oktruepipeline(Some(Error"une erreur"));;-:(bool,string)result=Erroru"Houla y'a eu un truc: une erreur"
P.S : sinon sympa le journal, et comme depuis les derniers journaux sur la virgule flottante j'ai installé GNAT et GNAT Programming Studio je vais pouvoir regarder un code ADA idiomatique et jouer avec :-) (ravi de voir au passage qu'il y a des développeurs ADA qui comprennent l'encapsulation ;-).
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
Pourquoi vous n'avez jamais trouvé un moyen d'éviter d'utiliser les insupportables opérateurs float (+.),(-.),(/.),(*.) ?
Il y a une proposition pour résoudre ce problème, et offrir bien plus de possibilité, sous la forme de modules implicites. Il y a eu une discussion sur le sujet sur le forum OCaml, et il reste des questions tant théoriques que pratiques (au niveau de l'implémentation) à résoudre avant de voir apparaître le système dans le langage.
Pour faire simple, l'idée est de passer par des modules de premières classes et, dans le cas de ces opérateurs, de définir, disons, un type de module pour les corps :
Ici le corps à passer en paramètre est explicite et si on utilise l'alternative Batteries à la bibliothèque standard, on peut écrire :
add(moduleFloat)2.34.5;;-:float=6.8
L'idée étant de faire du module de première classe un paramètre implicite déterminé automatiquement par le compilateur en fonction du type des paramètres x et y. Ici comme ce sont des float, le compilateur cherchera dans son environnement au moment de l'appel à add un corps sur les float déclaré utilisable comme arguments implicites.
En attendant, si tu utilises Batteries tu peux écrire ton code sur les flottants via des open locaux :
Ltac est un bousin sans nom qui est la source d'un tas de problèmes, le genre de code qu'il suffit de regarder fixement pour faire péter un développement à l'autre bout de la planète.
Justement, au sujet de Ltac, vous avez des idées ou des pistes pour l'améliorer et faciliter l'écriture de tactiques ? Si on prend l'exemple de patrick_g :
Par exemple dans de nombreux articles de géométrie arithmétique ou de géométrie algébrique on peut voir des phrases du style : "Par un argument à la GAGA il est facile de voir que blabla".
il doit bien être possible d'écrire une tactique gaga, mais ce possible reste souvent « théorique », écrire une tactique finissant par rendre à moitié fou (j'ai jamais bien compris comment marchait le système).
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
Et tu n'as pas parlé d'une partie qui me semble prendre de plus en plus de temps dans le monde académique : la recherche de financement.
Pour le langage développé au sein de sa nouvelle équipe, on trouve ces informations sur le site du langage. À l'origine, le projet fut financé par l'entreprise Boston Scientific spécialisé dans le matériel médical, puis par des fonds publics provenant de la National Science Foundation (équivalent américain du CNRS).
Je me demande, d'ailleurs, si le nom du langage (Abella) ne vient pas du premier financeur : l'entreprise a été cofondée par John Abele. Au départ (j'aime bien essayé de trouver l'origine des noms des projets) je pensais que le nom faisait référence au logicien de l'époque scolastique Pierre Abélard car il est souvent utilisé en théorie de la démonstration avec sa bien aimée Héloïse (comme dans l'article Formules valides, jeux et protocoles réseau qui utilise des principes identiques à ceux dont parle gasche dans son journal, mais pour la certification de protocoles réseau), mais l'hypothèse de John Abele me semble plus probable.
démystifier l'activité de recherche et montrer à quoi « ça sert » puisque l'on vit dans un monde où la finalité semble importante à beaucoup :-/
Il en a été de tout temps ainsi, et une citation du maître pour la route :-)
Il n'est point de connaissance qui soit superflue et inutile de façon absolue et à tous égards, encore que nous ne soyons pas toujours à même d'en apercevoir l'utilité. C'est par conséquent un objection aussi mal avisée qu'injuste que les esprits superficiels adressent aux grands hommes qui consacrent aux sciences des soins laborieux lorsqu'ils viennent demander : à quoi cela sert-il ? On ne doit en aucun cas poser une telle question quand on prétend s'occuper de science. À supposer qu'une science ne puisse apporter d'explication que sur un quelconque objet possible, de ce seul fait son utilité serait déjà suffisante. Toute connaissance logiquement parfaite a déjà quelque utilité possible : même si elle nous échappe jusqu'à présent, il se peut que la postérité la découvre. Si en cultivant les sciences on n'avait jamais mesuré l'utilité qu'au profit matériel qu'on pourrait retirer, nous n'aurions pas l'arithmétique et la géométrie. Aussi bien notre intelligence est ainsi conformée qu'elle trouve satisfaction dans la simple connaissance, et même une satisfaction plus grande que dans l'utilité qui en résulte. Platon l'avait déjà remarqué. L'homme y prend conscience de sa valeur propre; il a la sensation de ce qui se nomme : avoir l'intelligence. Les hommes qui ne sentent pas cela doivent envier les bêtes. La valeur intrinsèque que les connaissances tiennent de leur perfection logique est incomparable avec leur valeur extrinsèque, qu'elles tirent de leur application.
Emmanuel Kant, Logique
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
En tout cas, gasche suit la graphie de sa nouvelle équipe — qui n'a peut être pas mis à jour sa page de présentation depuis le changement, à la lecture des dates qui y figurent.
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: Commentaire bookmark
Posté par kantien . En réponse au journal "Intelligence artificielle", vraiment?. Évalué à 3.
Personne n'achèterait une voiture pour laquelle ils seraient considérés comme responsables en cas d'accident (surtout s'il cause la mort d'un tiers) alors qu'ils ne peuvent rien y faire. ;-)
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: Fonctionnel vs Impératif
Posté par kantien . En réponse au lien État des lieux des langages fonctionnels. Évalué à 2. Dernière modification le 04 avril 2018 à 17:52.
Ou alors, pour être plus générique sur la conception des itérateurs, utiliser des foncteurs applicatifs : The Essence of the Iterator Pattern.
Effectivement, voir le chapitre 1 de ce cours de programmation avancée pour la comparaison entre Java et OCaml sur la manière de définir des types algébriques.
Avec des
goto
? :-POk,
---> []
…Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: Association/SARL
Posté par kantien . En réponse à la dépêche LinuxFr.org sera impliqué dans le plan français sur l’intelligence artificielle #FranceIA. Évalué à 8. Dernière modification le 01 avril 2018 à 11:37.
Je ne comprends pas ton interrogation. Le statut de Société Anthropomorphique des Rascasses et Limandes était bien sûr le plus adapté pour la SOLE (Search Of Learning Events).
Pour le chiffre d'affaire qui semble t'inquiéter, le statut prévoit explicitement que s'il est de
42
et que la SOLE réussi à le décomposer en facteurs premiers (exploit inédit à ce jour) la SARL sera exonérée de charges ad vitam æternam pour service rendu à la patrie : cela évitera aux juges d'avoir à demander les clés de chiffrement.Nul doute qu'à partir de l'excellence de la base de données dont dispose la SOLE, elle mènera a bien cet exploit. Il paraîtrait même qu'elle aurait déjà trouvé les facteurs
7
et3
en s'écriant : « en ce septième jour de la semaine, dimanche de Pâques, louons la sainte trinité notre Père, son Fils et le Saint Esprit et célébrons la résurrection de notre Seigneur ». L'équipe en charge de la superviser redoute tout de même une possible dérive vers un délire mystique et cabalistique : une enquête est en cours.Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: Deep learning
Posté par kantien . En réponse à la dépêche LinuxFr.org sera impliqué dans le plan français sur l’intelligence artificielle #FranceIA. Évalué à 8. Dernière modification le 01 avril 2018 à 08:51.
Afin de lever ses doutes, la communauté scientifique a demandé à l'IA de se représenter elle-même :
Le verdict n'a pas fait une plie : nous avons bien affaire à une intelligence de tout premier ordre. Le risque est grand que l'être humain soit bientôt déclaré obsolète.
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: Icones bureau
Posté par kantien . En réponse à la dépêche GNOME 3.28. Évalué à 4.
Aller dans le menu de configuration du clavier, chercher « Masquer toutes les fenêtres normales » dans la section navigation, puis définir le raccourci à
Super + D
.Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: Icones bureau
Posté par kantien . En réponse à la dépêche GNOME 3.28. Évalué à 5.
Super + h
pour cacher (hide) une fenêtre.Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: Ou pas
Posté par kantien . En réponse au journal TapTempo en PHP. Évalué à 2.
Après réflexion, vu les contraintes du programme, le mieux serait sans doute de faire :
avant de rentrer dans la boucle, et :
pour rétablir le terminal dans un état sain avant de quitter le programme (cf
man stty
pour les explications).Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: Lisibilité
Posté par kantien . En réponse au journal Portage de TapTempo en OCaml. Évalué à 3.
Absolument aucune limitation si les appels récursifs sont terminaux : il se font alors en espace constant sur la pile. Voir ma réponse du dessous à gndl.
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: Lisibilité
Posté par kantien . En réponse au journal Portage de TapTempo en OCaml. Évalué à 4. Dernière modification le 15 mars 2018 à 11:45.
Merci.
Tu parles de la documentation officielle ? Celle-ci est plus un manuel de référence du langage qu'une initiation aux principes de la programmation fonctionnelle.
La traduction de la boucle
while
que j'ai faite relève des principes généraux de la programmation fonctionnelle. C'est pour cela que j'avais donné un lien vers un de mes commentaires sur le journal de la version detaptempo
en Emacs Lisp. Une personne cherchait à écrire la fonction factorielle de manière récursive terminale et ne savait pas comment faire. Il avait écrit la version naïve :qui génère un dépassement de pile sur de grandes entrées :
La version impérative pour une telle fonction, à base de boucle
for
, est la suivante :La version fonctionnelle avec récursion terminale consiste donc à utiliser un boucle avec un accumulateur, comme dans la version impérative :
Dans les deux versions, impérative et fonctionnelle, la boucle dépend de l'entrée
n
. Mais, dans la version fonctionnelle, l'accumulateur est également un paramètre de la boucle, là où c'est une variable globale pour celle-ci dans le cas impératif.Pour l'autre transformation du code, là c'est plutôt une astuce propre aux langages fonctionnels qui permettent d'avoir des opérateurs binaires infixes, donc hors famille Lisp et leur folie des parenthèses. L'idée étant que dans une telle situation :
on puisse « factoriser » la variable sur laquelle on effectue notre séquence de transformation :
Ici, il faut pouvoir définir les opérateurs d'ordre supérieur
&
et|-
, ce que peut faire n'importe quel langage fonctionnel, mais leur utilité réside essentiellement dans le fait qu'on les utilise de manière infixe, ce qui fournit du sucre syntaxique à cette approche.Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: Lisibilité
Posté par kantien . En réponse au journal Portage de TapTempo en OCaml. Évalué à 5. Dernière modification le 13 mars 2018 à 17:49.
Comme tu es plus habitué au paradigme impératif, je donne ci-dessous la boucle
while
équivalente à mon code avec une fonction récursive, puis je donne une explication pour passer de l'un à l'autre.Le principe d'une boucle
while
, comme illustré dans cet exemple, est d'être une procédure qui modifie par effet de bords certaines variables qui lui sont globales (ici essentiellementsamples
etstamp
) tant qu'une condition est satisfaite (ici, tant que l'on n'a pas appuyé surq
ou que l'entrée standard est n'est pas au bout). En tant que telle, du point de vue d'OCaml, c'est une expression qui a une valeur (comme n'importe quelle expression du langage) de typeunit
: ce type ne contient qu'une seule valeur (on parle de type singleton) notée()
, c'est la valeur qui ne contient aucune information.Pour transformer cette boucle en une fonction récursive, il faut d'abord qu'elle retourne la même valeur, à savoir
()
, lorsqu'elle termine. Ensuite, les variables globales sur laquelle opérait la boucle sont transformées en paramètres de la fonction : elles ne seront plus globales à la boucle, mais locales. On commence donc par écrire :Ensuite, il faut exprimer la condition d'arrêt de la boucle. Celle-ci dépend du caractère que l'on lit sur l'entrée standard, d'où :
On fait alors une étude de cas, à la manière d'un switch, en commençant par dire quand la fonction termine (et qui renvoie alors
()
) :Vient ensuite le cœur de la logique de la boucle, ce qui se passe quand on a appuyé sur
enter
:Il reste enfin à traiter le cas où l'on a appuyé sur une autre touche : on ne fait rien et on relance la boucle avec les mêmes paramètres
Maintenant que le corps de la fonction est écrit, il ne reste plus qu'à l'appeler pour lancer la boucle. Pour ce faire, on appelle la fonction avec, pour paramètres, les valeurs initiales des variables globales de la boucle
while
:Voilà le principe général pour transformer une boucle
while
en fonction récursive : on transforme les variables globales de la boucle en variables locales, et à chaque tour on lui passe les nouvelles valeurs.Ceci étant, ce genre d'approche n'est pas propre au paradigme fonctionnel, mais ce dernier en fait un usage omniprésent et c'est la route vers la pureté (absence d'effets de bords)1. Pour l'instant, la variable
samples
fonctionne toujours pas effet de bords et, dans le message précédent, j'ai juste modifié l'écriture de sa logique de mise à jour en utilisant une approche par pipe avec le code :Mais si, à la place d'une structure impérative, j'utilisais une structure purement applicative, j'aurais juste à changer les deux premiers combinateurs de tuyaux : un pipe
|>
au lieu d'un T|-
et utiliser la fonctionidentity
au lieu deignore
Derrière cette vision par pipeline, il y a une notion élémentaire de mathématique (bon c'est pas du niveau primaire, mais début de lycée ;-), à savoir la composition de fonction.
que l'on peut écrire (la pipeline est assez visible sur le diagramme) :
L'opérateur de composition est une opérateur d'ordre supérieur : il prend deux fonctions en entrée et en renvoie une en sortie; raison pour laquelle il a une place centrale de le paradigme de la programmation fonctionnelle. Que peux-t-on faire avec une fonction ?
J'espère que ces explications t'aideront un peu mieux à comprendre certains principes élémentaires à la base du paradigme fonctionnel.
En réalité, c'est cette recherche d'absence d'effets de bord qui nous les fait écrire ainsi. On obtient alors des fonctions récursives avec appel dits terminaux, que le compilateur optimisera comme une simple boucle (il fera la traduction dans le sens inverse de celle que je viens de faire). Voir mon commentaire sur la version de taptempo en Emacs Lisp. ↩
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: Intéressant mais ....
Posté par kantien . En réponse au journal upt: l'outil parfait pour empaqueter TapTempo. Évalué à 4. Dernière modification le 13 mars 2018 à 17:34.
Comme les compilateurs qui se respectent et qui se compilent eux-mêmes, n'importe quelle version d'upt pourra packager toutes les autres y compris elle-même. :-D
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: Ou pas
Posté par kantien . En réponse au journal TapTempo en PHP. Évalué à 5. Dernière modification le 13 mars 2018 à 10:46.
Cela étant tu devrais peut être faire un appel à
stty icanon
avant de quitter ton programme, pour remettre le terminal dans sa configuration initiale.Pour l'option
echo
, tu peux jouer avec cette commande dans ton terminal :Attention : après on ne voit plus à l'écran ce que l'on tape (pratique pour la saisie d'un mot de passe ;-), il faut taper à l'aveugle la commande :
pour remettre les choses en ordre. :-)
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: Bouh !
Posté par kantien . En réponse au journal TapTempo en emacs lisp. Évalué à 6. Dernière modification le 13 mars 2018 à 10:38.
Avec un accumulateur ;-)
Je ne suis pas un expert en Elisp, mais ça doit ressembler à cela :
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: Ou pas
Posté par kantien . En réponse au journal TapTempo en PHP. Évalué à 3.
C'est le fonctionnement canonique (d'où le nom de l'option ;-) de l'entrée standard d'un terminal dans les systèmes Unix.
man termios
pour de plus amples informations (en désactivant la fonctionecho
, l'entrée standard ne renvoie pas ce qu'elle reçoit sur la sortie standard, par exemple).Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: Lisibilité
Posté par kantien . En réponse au journal Portage de TapTempo en OCaml. Évalué à 6.
Dans ce cas, je te propose la version suivante de la boucle principale (dans une approche similaire à celle en Elixir et ce que l'auteur à appeler flow programming).
Ici c'est dans la même veine que le code Rust que tu ne comprenais pas : c'est à base de pipe (on connecte les flux d'entrée et de sortie de fonctions). Il y a deux types de connecteurs de flux sous forme d'opérateurs infixes :
|-
qui est proche de la commande unixtee
(d'où le choix du symbole) et le|>
qui fonctionne comme le pipe|
du shell.Si tu comprends cette commande, alors c'est bon :
on chaîne les commandes, et on utilise
tee
pour faire un log de résultats intermédiaires mais sans casser le flux. Comme dans cet exemple OCaml :on prend une chaîne, on lui retire les espaces en préfixe et suffixe (
String.trim
), on calcule la longueur de la chaîne ainsi obtenue (String.length
) puis on effectue des calculs sur cette valeur entière, le tout en faisant quelques commandes de log entre temps. Exemple d'usage :L'opérateur infixe
|-
se définit tout simplement ainsi :j'ai juste mis une contrainte de type sur la sortie de
f
de telle sorte que :Maintenant, si on reprend la partie du code qui correspond au traitement à effectuer lors de l'appui sur la touche
enter
:On calcule un nouvelle horodatage et le temps écoulé (en secondes) depuis le dernier appui sur
enter
; ensuite on enchaîne les traitements sur la file des horodatages :option
contenu dans la structure[1]) ;C'est beau, c'est fin, ça se mange sans faim. :-)
[1]: en arrière plan, le type de la file d'horodatage est
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: ISO-fonctionnel
Posté par kantien . En réponse au journal Portage de TapTempo en OCaml. Évalué à 4.
Sans avoir à installer une partie des outils de développements OCaml, le plus simple est sans doute de passer par le gestionnaire de paquets de ta distribution. Sous Debian (ou dérivée), il te faudrait au minimum les paquets suivants :
ensuite tu vas dans le répertoire des sources, et tu lances la commande suivante :
Par contre, chez moi, l'internationalisation ne marche pas : j'ai le texte en anglais. De plus, le flux de sortie standard n'est pas vidé sur certaines écritures : je ne vois rien avant d'avoir appuyer plusieurs fois sur
enter
. Tu peux corriger cela en rajoutantflush stdout;
après ces deux lignes detaptempocaml.ml
:Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: Lisibilité
Posté par kantien . En réponse au journal Portage de TapTempo en OCaml. Évalué à 3. Dernière modification le 12 mars 2018 à 10:41.
Qu'entends-tu par plus OCaml ? Tu veux dire dans un style fonctionnel ? Là son code est fondamentalement impératif (le langage est multi-paradigme) et les traits impératifs se prêtent bien au problème. Ceci étant on pourrait ne pas mettre de boucle
while
, mais une fonction récursive à la place, du genre :c'est ce genre de code dont tu parles ? (ici j'utilise une horloge monotone et un module à ma sauce pour gérer la logique de calcul du tempo)
L'avantage que je vois, c'est que cela évite les imbrications, des fois dure à suivre, des
if then else
. Ici on est protégé des exceptions et seule la toucheenter
peut être appuyée en cadence. Sans cela, un test comme :générera une exception (non rattrapée) quand le flux d'entrée est fermé et, selon que l'on accepte ou non n'importe quel caractère, cela peut changer le résultat de celui-ci :
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: Sympa ton journal
Posté par kantien . En réponse au journal Des vieilles bases d'unix à la hype reactive actuelle. Évalué à 2.
Avec l'extension de syntaxe de Lwt aussi ;-).
Dans
utop
, il suffit de faire :Pour une version avec
bind
, proche de la syntaxe NodeJS, où on passe la promesse à une fonction anonyme :La même, mais avec l'extension de syntaxe : on retrouve une forme proche de celle d'un code synchrone (les
let x = e in
sont remplacés par deslet%lwt x = e in
) :Il y a une forme (à base de
%lwt
à rajouter) pour presque toutes les constructions syntaxiques du langage.Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: Sympa ton journal
Posté par kantien . En réponse au journal Des vieilles bases d'unix à la hype reactive actuelle. Évalué à 4.
Bah, c'est la monade identité, qui n'est pas spécialement la plus utile.
Par contre, je comprends pas trop où veut en venir Michaël : avec les monades et leur
bind
, on les utilise plutôt dans le sens inverse de la composition (à la manière du pipe du shell).Là j'ai trois types A, B et C et deux fonctions
f : A -> B
etg : B -> C
. Si je les compose, j'obtiens une fonction de A vers C. On peut le dire en mode direct (à la mode mathématique) : appliqueg
au résultat def
; ou en mode inverse : passe le résultat def
àg
. La conception à la manière d'une pipeline, c'est de le voire comme sur le dessin en mode inverse : on écrit les traitements dans l'ordre où ils sont effectués (f
puisg
).Après une monade, c'est un type paramétrique (ici
F
sur le dessin) avec certaines bonnes propriétés. Tout d'abord on doit pouvoir faire unmap
dessus : à partir def : A -> B
, on peut construiremap f : F(A) -> F(B)
qui respecte la composition. Si je compose f et g, puis que j'applique map, ou que j'applique map puis que je compose, on doit obtenir le même résultat.Ensuite, pour que ce type paramétrique soit une monade, il faut pouvoir combiner une fonction
A -> F(B)
avec un autreB -> F(C)
: ce sont les diagonales que l'on cherche à assembler. Mais pour ce faire, on utilise plutôt l'opérateurbind
(ou>>=
en notation infixe) qui prend une valeur de typeF(A)
, une fonction de typeA -> F(B)
et renvoie une valeur de typeF(B)
. C'est l'équivalent du pipe pour les monades : le pipe est à la composition de fonctions, ce que le bind est à la composition d'opérateurs monadiques.Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: L'infini
Posté par kantien . En réponse au journal Portage de TapTempo en Perl6. Évalué à 2.
Difference between CLOCK_REALTIME and CLOCK_MONOTONIC? ;-)
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: Merci !
Posté par kantien . En réponse au journal Portage de TapTempo en Ada. Évalué à 5. Dernière modification le 28 février 2018 à 18:41.
C'est parce que la pipeline est courte. Il faut voir cette notation pointée de Rust comme le pipe
|
du shell :reader.next() | unwrap_or | map_err | map
. Sur de longues pipelines, c'est plus simple à écrire et il vaut mieux laisser le compilateur inliner le tout plutôt que de le faire à la main (comme dans sa version verbeuse, qui contient d'ailleurs une erreur : le casNone
doit retournerOk(false)
de typeResult<Bool, String>
).En OCaml (j'étais bien obligé), on écrirait un code du genre :
Les patterns à base de
map
sont omniprésents dans le paradigme fonctionnel. Un type paramétrique à un paramètre (comme les options, les listes, les tableaux, les vecteurs…) est une fonction des types dans les types. Ainsi si j'ai une fonction qui transforme les objets du paramètre (disons desint
enstring
), je peux la lifter pour opérer sur le type paramétrique (un peu comme une composition de fonction, si tu veux). Exemples :Le type
Result
est lui un type paramétrique à deux paramètres : on peut donc faire unmap
soit sur le type en paramètre deOk
, soit sur celui en paramètre deErr
. Ce qui soit donne deux fonctionsmap
(comme en Rust), soit une fonctionmap_both
comme dans mon exemple en OCaml (oubimap
en Haskell) qui prend deux fonctions en paramètres (une pour chaque type).P.S : sinon sympa le journal, et comme depuis les derniers journaux sur la virgule flottante j'ai installé GNAT et GNAT Programming Studio je vais pouvoir regarder un code ADA idiomatique et jouer avec :-) (ravi de voir au passage qu'il y a des développeurs ADA qui comprennent l'encapsulation ;-).
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: Ocaml et Float
Posté par kantien . En réponse au journal La recherche en langages de programmation au quotidien. Évalué à 3.
Il y a une proposition pour résoudre ce problème, et offrir bien plus de possibilité, sous la forme de modules implicites. Il y a eu une discussion sur le sujet sur le forum OCaml, et il reste des questions tant théoriques que pratiques (au niveau de l'implémentation) à résoudre avant de voir apparaître le système dans le langage.
Pour faire simple, l'idée est de passer par des modules de premières classes et, dans le cas de ces opérateurs, de définir, disons, un type de module pour les corps :
puis à définir, disons une fonction
add
, qui opère sur n'importe quel corps et des valeurs du support du corps :Ici le corps à passer en paramètre est explicite et si on utilise l'alternative
Batteries
à la bibliothèque standard, on peut écrire :L'idée étant de faire du module de première classe un paramètre implicite déterminé automatiquement par le compilateur en fonction du type des paramètres
x
ety
. Ici comme ce sont desfloat
, le compilateur cherchera dans son environnement au moment de l'appel àadd
un corps sur lesfloat
déclaré utilisable comme arguments implicites.En attendant, si tu utilises
Batteries
tu peux écrire ton code sur les flottants via desopen
locaux :voir la définition du module
Float.Infix
:Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: Perl6
Posté par kantien . En réponse au journal La recherche en langages de programmation au quotidien. Évalué à 2. Dernière modification le 09 février 2018 à 21:26.
Justement, au sujet de Ltac, vous avez des idées ou des pistes pour l'améliorer et faciliter l'écriture de tactiques ? Si on prend l'exemple de patrick_g :
il doit bien être possible d'écrire une tactique
gaga
, mais ce possible reste souvent « théorique », écrire une tactique finissant par rendre à moitié fou (j'ai jamais bien compris comment marchait le système).Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: Passionant
Posté par kantien . En réponse au journal La recherche en langages de programmation au quotidien. Évalué à 7. Dernière modification le 07 février 2018 à 11:47.
Pour le langage développé au sein de sa nouvelle équipe, on trouve ces informations sur le site du langage. À l'origine, le projet fut financé par l'entreprise Boston Scientific spécialisé dans le matériel médical, puis par des fonds publics provenant de la National Science Foundation (équivalent américain du CNRS).
Je me demande, d'ailleurs, si le nom du langage (Abella) ne vient pas du premier financeur : l'entreprise a été cofondée par John Abele. Au départ (j'aime bien essayé de trouver l'origine des noms des projets) je pensais que le nom faisait référence au logicien de l'époque scolastique Pierre Abélard car il est souvent utilisé en théorie de la démonstration avec sa bien aimée Héloïse (comme dans l'article Formules valides, jeux et protocoles réseau qui utilise des principes identiques à ceux dont parle gasche dans son journal, mais pour la certification de protocoles réseau), mais l'hypothèse de John Abele me semble plus probable.
Il en a été de tout temps ainsi, et une citation du maître pour la route :-)
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: c'est bien joli, mais…
Posté par kantien . En réponse au journal La recherche en langages de programmation au quotidien. Évalué à 5.
C'est une boutade au sein de l'Institut depuis qu'ils ont changé leur identité visuelle ?
En tout cas, gasche suit la graphie de sa nouvelle équipe — qui n'a peut être pas mis à jour sa page de présentation depuis le changement, à la lecture des dates qui y figurent.
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.