Au niveau du système de module, des annotations de types sont nécessaires pour les arguments des foncteurs:
moduleF(X:sigtypetend(* ← ceci est une annotation de type *))=structincludeXend(* mais le type (i.e. la signature) de retour est inferré *)
Les modules récursifs eux ont besoin d'être totalement annoté, par exemple:
modulerecM:(* ↓ annotation du type de M *)sigtypet=AofS.tvalcompare:t->t->intend=structletcompare=comparetypet=AofS.tendandS:(Set.Swithtypeelt=M.t)(* ← annotation du type de S *)=Set.Make(M);;
Ce besoin d'annotation pour les modules peut se propager dans les fonctions à cause
des modules locaux (ou de première classe).
letdedup(typea(* ←soit un type a abstrait*))l=(* ici on a besoin d'introduire un type a pour le module S définit ci-dessous *)letmoduleS=Set.Make(structtypet=aletcompare=compareend)inS.elements@@S.of_listl
Un autre cas classique d'annotation explicite apparaît pour la récursion polymorphique
sur les types récursifs non homogènes:
type'aarbre_complet=Feuilleof'a|Noeudof('a*'a)arbre_completletrechauteur:'a.(*← ∀a *)'aarbre_complet->int=function|Feuille_->0|Noeuda->(* dans cette branche, hauteur est appliqué à ('a * 'a) arbre_complet *)1+hauteura
Dans ce cas, l'annotation 'a.'a arbre_complet -> int qui équivaut à forall 'a.…
est nécessaire car le type du paramètre 'a à l'intérieur de la définition de hauteur et à l'extérieur diffère.
Pour le polymorphisme de second ordre, OCaml ne supporte pas directement les annotations forall à l'intérieur des arguments de fonctions, il faut donc passer soit par des types enregistrements(records):
typeid={f:'a.'a->'a}
ce qui est plus contraignant qu'une annotation directe, soit par le système objet
letpair(id:<f:'a.'a->'a>)=id#f1,id#f[]
Sans cette annotation le typeur va essayer d'unifier le type de 1 et [] et renvoyer une erreur.
Les GADTs de leur côté sont toujours lourds en annotations en Haskell ou OCaml
type'amonoid=|R:floatmonoid|N:intmonoid|Pair:'amonoid*'bmonoid->('a*'b)monoidletreczero:typea.(*← ∀a, a abstrait *)amonoid->a=function|R->0.(* ← dans cette branche, a = float *)|N->0(* ← a = int *)|Pair(a,b)->zeroa,zerob(* a = b * c *)
Enfin, une particularité d'OCaml du aux effets de bords, ce sont les types faiblement polymorphiques, qui ne peuvent pas être exporté par une unité de compilation, ce qui peut nécessiter d'ajouter des annotations de type pour les éliminer. Par exemple, un fichier a.ml
C'était pas pour critiquer Haskell, c'était juste pour pour souligner cette différence entre Haskell et OCaml.
Ce n'est pas vraiment une différence entre OCaml et Haskell, en OCaml aussi les usages avancés du système de type requièrent des annotations de type explicite: que ce soit au niveau des modules et foncteurs, des types algébriques généralisés, du polymorphisme de second ordre, ou de la récursion polymorphique.
Il est peut être plus courant de se heurter à ces problèmes en Haskell, mais le problème sous-jacent reste le même: au-delà d'un certain niveau d'expressivité du système de type, l'inférence devient indécidable, et le compilateur a besoin que le programmeur explicite son intention.
Et s'ils ne sont pas sensibilisés, on veut juste qu'ils apportent des critiques valables et argumentées
Mais évaluer son niveau d'incompétence dans un domaine est une des choses les plus difficiles à faire, quel que soit le domaine.
Sur un projet qui manque de direction en terme de design, je m'attend justement à ce que la capacité à jauger de la qualité d'un design et prendre des décisions à ce niveau soit tout aussi manquante ou embryonnaire.
Bref, on ne veut pas de traitement de faveur, on veut juste être traité comme les autres contributeurs : que les contributions soient jugées, acceptées ou refusées par des gens au même niveau au moins que ceux qui les soumettent.
Mais dans un projet qui manque de designers, c'est par définition une requête impossible à satisfaire. Exigez de n'être jugé que par d'autres designers pour une contribution à un projet qui n'a pas de designer ; ce n'est même pas un mauvais argument d'autorité, c'est un plan de communication absolument désastreux.
L'intérêt que je vois au type fantôme, c'est qu'ils permettent de rafiner un type de base, en rajoutant potentiellement de l'information connue à la compilation, et ensuite de définir des jeux de fonctions qui utilisent −optionnellement− cette information supplémentaire, le tout en gardant explicite qu'il s'agit du même type de base.
Par exemple, on pourrait imaginer un type liste augmenté d'une information sur le fait qu'elle soit triée ou non
À partir de là, je peux raffiner les fonctions sur les listes pour rajouter de l'information:
valsort:('a,'triee_ou_pas)liste->('a,triee)liste(** triée une liste la rend triée *)valrev:('a,'tree_ou_pas)liste->('a,non_triee)liste(** renversée une liste triée ou non, donne une liste non triée *)
Cela permet aussi de définir des fonctions qui requiert en entrée des listes triées, ce que le type fantôme permet de garantir
valaffiche_triee:(int,triee)liste->unit
Mais la grande différence par rapport à avoir deux types abstraits, c'est qu'il est beaucoup plus simple de partager les fonctions entre les différents sous-types dans les cas où ces fonctions n'ont pas besoin de l'information supplémentaire:
valiter:('a->unit)->('a,'triee_ou_pas)liste->unit
Un autre point sur lesquels des types fantômes peuvent être intéressant, ce sont les bindings avec des bibliothèques extérieures en C. Les types fantômes peuvent permettre d'introduire un typage correct sans trop s'éloigner de la représentation en C. Par exemple, j'avais utilités dans un prototype de bindings OpenGL des types fantômes pour typer les enums et handles opengl, qui sont tous des entiers dans la représentation C. Ce qui permet de transformer
et de s'assurer que système de type d'OCaml vérifiera à notre place que seulement des identifiants valides seront passés à l'API OpenGL; tout en pouvant facilement repasser à des entiers non typés au besoin.
Pour ce cas, ça doit pouvoir se faire avec des GADT
Cela dépend beaucoup ce que tu entends par "ce cas" et pouvoir se faire:
il est possible d'encoder les entiers naturels dans le système de type d'OCaml, avec des GADTs ou des types fantômes; cependant la complexité de l'encodage a tendance à s'accroître rapidement avec l'expressivité dudit encodage.
la taille du type augmente linéairement avec l'entier, ce qui peut créer des problèmes pour le compilateur assez rapidement ( il avait tendance à planter vers 65000 lors de mes derniers tests? )
les opérations que l'on peut effectuer sont très limitées.
Mais il peut être suffisant pour encoder un certain nombre d'invariants en algèbre linéaire (par exemple, on peut vérifier que les dimensions des matrices dans le produit matriciel soient correctes). Par contre, si je souhaite concaténer deux vecteurs, j'aurai
besoin d'encoder l'addition d'entier dans mon type 'nat1 t -> 'nat2 t -> ('nat1 + 'nat2) t?
Cependant, avec cet encodage, le système de type n'a pas assez d'information pour construire le type 'nat1 + 'nat2, il faut soit passer par des types auxiliaires, ou modifier l'encodage des entiers. Un exemple qui marche est d'encoder des propositions
de la forme a + b = c:
type'natt=|Z:('a*'a)t(* 'a + z = 'a*)|S:('z,'a)t->('z,'asucc)t(* si 'n + a = b alors 'n + succ a = succ b *)
À partir de cet encodage, on peut écrire l'addition
Cependant, le paramètre de type libre 'a a tendance à créer des problèmes du au fait que le GADT n'est pas covariant, donc la relaxed value restriction ne s'applique plus et
il est très (trop) facile de se retrouver avec des types faibles.
Et la complexité ne fait qu'empirer si on essaie d'implémenter d'autres fonctionnalités (représentation binaire, multiplication, comparaison, etc …) sur les entiers dans le système de types.
Un bon exemple de ce qui peut être fait raisonnablement dans le cadre de l'algèbre linéaire est SLAP.
Un exemple pédagogique pour montrer à quel point le budget de complexité peut exploser facilement serait ma bibliothèque expérimentale tensority.
Utiliser des types fantômes peut fonctionner, il faut faire cependant attention à quelles égalités de types on rend visible.
Effectivement, si on exporte l'égalité complète
type'at=int*int
il est possible de jongler entre les différentes valeurs du paramètre fantôme.
letx:intt=0,1lety:floatt=x
Mais c'est du au fait que le système de type se rappelle qu'au final 'a t ≡ int * int.
Pour éviter ce problème, on peut cacher cette égalité de type, soit partiellement
Faire une traduction en anglais pourrait être intéressant. Mais une bonne partie du contenu existe déjà en version anglaise ; de manière plus dispersée il est vrai. Peut-être qu'un bon compromis serait de traduire le contenu non publié par ailleurs (ou uniquement dans le manuel) : par exemple, la section sur les GADTs, celle sur les attributs et nœud d'extension prédéfinis, ou la longue liste des améliorations mineures.
Je n'ai été jamais complètement convaincu par la notation do comparé à utiliser directement >>= et >>.
La notation do est certainement plus élégante, mais bien moins explicite. Pour en revenir à OCaml, il faut tout de même noter qu'il est parfaitement possible d'utiliser >>= plutôt que bind. De même, il existe de nombreuses extensions de syntaxe qui permettent d'utiliser la notation do en OCaml; j'en compte au moins 4 disponibles directement avec opam.
Un standard supporté par les navigateurs? Dans mon souvenir, le support de MathML a été abandonné dans Chrome et n'est toujours pas planifié pour Internet Explorer.
Pour m'être brièvement intéressé à la question de l'analyse grammaticale, l'analyse syntaxique de langue naturelle est beaucoup plus compliquée que celle pratiquée dans les compilateurs: la plupart des grammaires des langues naturelles sont des grammaires contextuelles fort propices aux ambiguïtés. Pour le peu que je comprend le sujet, il semble exister une recherche fort active sur cette question autour des grammaires d'arbres adjoints et le développement de méta-grammaires pour générer automatiquement des grammaires à partir d'un corpus de texte.
Par contre, les applications pratiques de ces modèles pour faire de la correction grammaticale semblent manquer. Il est d'ailleurs un peu déconcertant de voir grammalecte utiliser des simples expressions régulières pour faire de l'analyse grammaticale. Enfin, le pragmatisme a ses vertus.
[^] # Re: Un peu déçu par Rust
Posté par octachron . En réponse au journal Un print(1 + "3a"), ça nous inspire comment ?. Évalué à 3.
Au niveau du système de module, des annotations de types sont nécessaires pour les arguments des foncteurs:
Les modules récursifs eux ont besoin d'être totalement annoté, par exemple:
Ce besoin d'annotation pour les modules peut se propager dans les fonctions à cause
des modules locaux (ou de première classe).
Un autre cas classique d'annotation explicite apparaît pour la récursion polymorphique
sur les types récursifs non homogènes:
Dans ce cas, l'annotation
'a.'a arbre_complet -> int
qui équivaut àforall 'a.…
est nécessaire car le type du paramètre
'a
à l'intérieur de la définition dehauteur
et à l'extérieur diffère.Pour le polymorphisme de second ordre, OCaml ne supporte pas directement les annotations
forall
à l'intérieur des arguments de fonctions, il faut donc passer soit par des types enregistrements(records):ce qui est plus contraignant qu'une annotation directe, soit par le système objet
Sans cette annotation le typeur va essayer d'unifier le type de
1
et[]
et renvoyer une erreur.Les GADTs de leur côté sont toujours lourds en annotations en Haskell ou OCaml
Enfin, une particularité d'OCaml du aux effets de bords, ce sont les types faiblement polymorphiques, qui ne peuvent pas être exporté par une unité de compilation, ce qui peut nécessiter d'ajouter des annotations de type pour les éliminer. Par exemple, un fichier
a.ml
ne compile pas sans son annotation de type.
[^] # Re: Un peu déçu par Rust
Posté par octachron . En réponse au journal Un print(1 + "3a"), ça nous inspire comment ?. Évalué à 2. Dernière modification le 04 avril 2017 à 20:13.
Ce n'est pas vraiment une différence entre OCaml et Haskell, en OCaml aussi les usages avancés du système de type requièrent des annotations de type explicite: que ce soit au niveau des modules et foncteurs, des types algébriques généralisés, du polymorphisme de second ordre, ou de la récursion polymorphique.
Il est peut être plus courant de se heurter à ces problèmes en Haskell, mais le problème sous-jacent reste le même: au-delà d'un certain niveau d'expressivité du système de type, l'inférence devient indécidable, et le compilateur a besoin que le programmeur explicite son intention.
[^] # Re: Mon commentaire sur le blog…
Posté par octachron . En réponse au journal Le libre et l'expérience utilisateur. Évalué à 5.
Mais évaluer son niveau d'incompétence dans un domaine est une des choses les plus difficiles à faire, quel que soit le domaine.
Sur un projet qui manque de direction en terme de design, je m'attend justement à ce que la capacité à jauger de la qualité d'un design et prendre des décisions à ce niveau soit tout aussi manquante ou embryonnaire.
[^] # Re: Mon commentaire sur le blog…
Posté par octachron . En réponse au journal Le libre et l'expérience utilisateur. Évalué à 7.
Mais dans un projet qui manque de designers, c'est par définition une requête impossible à satisfaire. Exigez de n'être jugé que par d'autres designers pour une contribution à un projet qui n'a pas de designer ; ce n'est même pas un mauvais argument d'autorité, c'est un plan de communication absolument désastreux.
[^] # Re: Type fantôme
Posté par octachron . En réponse au journal Une petite histoire d'utilisation type fort dans Ocaml. Évalué à 1.
L'intérêt que je vois au type fantôme, c'est qu'ils permettent de rafiner un type de base, en rajoutant potentiellement de l'information connue à la compilation, et ensuite de définir des jeux de fonctions qui utilisent −optionnellement− cette information supplémentaire, le tout en gardant explicite qu'il s'agit du même type de base.
Par exemple, on pourrait imaginer un type liste augmenté d'une information sur le fait qu'elle soit triée ou non
À partir de là, je peux raffiner les fonctions sur les listes pour rajouter de l'information:
Cela permet aussi de définir des fonctions qui requiert en entrée des listes triées, ce que le type fantôme permet de garantir
Mais la grande différence par rapport à avoir deux types abstraits, c'est qu'il est beaucoup plus simple de partager les fonctions entre les différents sous-types dans les cas où ces fonctions n'ont pas besoin de l'information supplémentaire:
Un autre point sur lesquels des types fantômes peuvent être intéressant, ce sont les bindings avec des bibliothèques extérieures en C. Les types fantômes peuvent permettre d'introduire un typage correct sans trop s'éloigner de la représentation en C. Par exemple, j'avais utilités dans un prototype de bindings OpenGL des types fantômes pour typer les enums et handles opengl, qui sont tous des entiers dans la représentation C. Ce qui permet de transformer
en
et de s'assurer que système de type d'OCaml vérifiera à notre place que seulement des identifiants valides seront passés à l'API OpenGL; tout en pouvant facilement repasser à des entiers non typés au besoin.
[^] # Re: Module et type abstrait
Posté par octachron . En réponse au journal Une petite histoire d'utilisation type fort dans Ocaml. Évalué à 2.
Cela dépend beaucoup ce que tu entends par "ce cas" et pouvoir se faire:
il est possible d'encoder les entiers naturels dans le système de type d'OCaml, avec des GADTs ou des types fantômes; cependant la complexité de l'encodage a tendance à s'accroître rapidement avec l'expressivité dudit encodage.
Par exemple, l'encodage le plus simple
fonctionne mais à plusieurs limitations majeures:
la taille du type augmente linéairement avec l'entier, ce qui peut créer des problèmes pour le compilateur assez rapidement ( il avait tendance à planter vers 65000 lors de mes derniers tests? )
les opérations que l'on peut effectuer sont très limitées.
Mais il peut être suffisant pour encoder un certain nombre d'invariants en algèbre linéaire (par exemple, on peut vérifier que les dimensions des matrices dans le produit matriciel soient correctes). Par contre, si je souhaite concaténer deux vecteurs, j'aurai
besoin d'encoder l'addition d'entier dans mon type
'nat1 t -> 'nat2 t -> ('nat1 + 'nat2) t
?Cependant, avec cet encodage, le système de type n'a pas assez d'information pour construire le type
'nat1 + 'nat2
, il faut soit passer par des types auxiliaires, ou modifier l'encodage des entiers. Un exemple qui marche est d'encoder des propositionsde la forme
a + b = c
:À partir de cet encodage, on peut écrire l'addition
Cependant, le paramètre de type libre
'a
a tendance à créer des problèmes du au fait que le GADT n'est pas covariant, donc la relaxed value restriction ne s'applique plus etil est très (trop) facile de se retrouver avec des types faibles.
Et la complexité ne fait qu'empirer si on essaie d'implémenter d'autres fonctionnalités (représentation binaire, multiplication, comparaison, etc …) sur les entiers dans le système de types.
Un bon exemple de ce qui peut être fait raisonnablement dans le cadre de l'algèbre linéaire est SLAP.
Un exemple pédagogique pour montrer à quel point le budget de complexité peut exploser facilement serait ma bibliothèque expérimentale tensority.
[^] # Re: Type fantôme
Posté par octachron . En réponse au journal Une petite histoire d'utilisation type fort dans Ocaml. Évalué à 3.
Utiliser des types fantômes peut fonctionner, il faut faire cependant attention à quelles égalités de types on rend visible.
Effectivement, si on exporte l'égalité complète
il est possible de jongler entre les différentes valeurs du paramètre fantôme.
Mais c'est du au fait que le système de type se rappelle qu'au final
'a t ≡ int * int
.Pour éviter ce problème, on peut cacher cette égalité de type, soit partiellement
soit complètement
Dans les deux cas, l'égalité de type n'est plus visible, et
est rejetée comme invalide.
[^] # Re: Merci
Posté par octachron . En réponse à la dépêche OCaml 4.03. Évalué à 2.
Faire une traduction en anglais pourrait être intéressant. Mais une bonne partie du contenu existe déjà en version anglaise ; de manière plus dispersée il est vrai. Peut-être qu'un bon compromis serait de traduire le contenu non publié par ailleurs (ou uniquement dans le manuel) : par exemple, la section sur les GADTs, celle sur les attributs et nœud d'extension prédéfinis, ou la longue liste des améliorations mineures.
[^] # Re: Notation do
Posté par octachron . En réponse au journal Compilateur et Monad Reader. Évalué à 1.
Je n'ai été jamais complètement convaincu par la notation
do
comparé à utiliser directement>>=
et>>
.La notation
do
est certainement plus élégante, mais bien moins explicite. Pour en revenir à OCaml, il faut tout de même noter qu'il est parfaitement possible d'utiliser>>=
plutôt quebind
. De même, il existe de nombreuses extensions de syntaxe qui permettent d'utiliser la notationdo
en OCaml; j'en compte au moins 4 disponibles directement avec opam.[^] # Re: Et l'inverse?
Posté par octachron . En réponse au journal Une nouvelle manière de publier des articles scientifiques ... HTML, métadonnées et linkeddata. Évalué à 1.
Un standard supporté par les navigateurs? Dans mon souvenir, le support de MathML a été abandonné dans Chrome et n'est toujours pas planifié pour Internet Explorer.
[^] # Re: On dirait de la compilation ... mais en plus difficile
Posté par octachron . En réponse à la dépêche Grammalecte, correcteur grammatical. Évalué à 8.
Pour m'être brièvement intéressé à la question de l'analyse grammaticale, l'analyse syntaxique de langue naturelle est beaucoup plus compliquée que celle pratiquée dans les compilateurs: la plupart des grammaires des langues naturelles sont des grammaires contextuelles fort propices aux ambiguïtés. Pour le peu que je comprend le sujet, il semble exister une recherche fort active sur cette question autour des grammaires d'arbres adjoints et le développement de méta-grammaires pour générer automatiquement des grammaires à partir d'un corpus de texte.
Par contre, les applications pratiques de ces modèles pour faire de la correction grammaticale semblent manquer. Il est d'ailleurs un peu déconcertant de voir grammalecte utiliser des simples expressions régulières pour faire de l'analyse grammaticale. Enfin, le pragmatisme a ses vertus.