C'est bien pour cela que je m'étonne de leur absence.
C'est pas étonnant qu'ils soient absents, puisque la pratique de programmation qu'ils couvrent est déjà couverte par le concept des interfaces (bien que ce ne soient pas des type somme implémentés de façon scolaire). Je donne un exemple plus bas pour les listes simplement chaînées.
Si j'ai bien compris, la valeur nil peut être affectée à n'importe quel type
Uniquement à un type pointeur *A (équivalent à option A), et il n'y a pas une seule valeur nil, mais une différente pour chaque type pointeur (A(nil) est différent de B(nil)). Et c'est en effet un type somme qui couvre le cas courant du type option.
l'on voit les 4 cas à gérer que déplorait Colin Pitrat dans son premier commentaire. Là où, avec un type somme de résultat, il n'y a que les deux cas A + B.
Sauf que les conventions font qu'il n'y a pas 4 cas en pratique, mais bien 2 sauf 3 dans de rares cas comme Read qui peut renvoyer une erreur EOF qu'on peut vouloir traiter de façon particulière (cas non gérable par un simple type somme A + B de toutes façons).
L'exemple que tu m'as donné pour les arbres ne semble pas faire de d'analyse de cas sur la représentation dynamique des types, mais définir des arbres dans un langage qui n'a pas de type somme (directement définissables et accessibles au programmeur) mais que des types produits (ou struct ou enregistrements...) comme on peut le faire en C, C++, Java, Python, etc.
Eh bien si, mais le fichier que j'ai donné contient uniquement la définition, pour les utilisations il faut regarder ailleurs comme ici qui implémente une marche sur l'ast en faisant un switch sur le type de l'interface qui sert à représenter un nœud.
Si on peut définir un ast, on peut définir en particulier une liste simplement chaînée :
packagemainimport"fmt"typeLinkedListinterface{ImplementsLinkedList()}typeNilstruct{}func(eNil)ImplementsLinkedList(){}typeIntListstruct{HeadintTailLinkedList}func(lIntList)ImplementsLinkedList(){}funcmain(){mylist:=IntList{Head:1,Tail:IntList{Head:2,Tail:Nil{}}}fmt.Printf("%+v\n",mylist)// affiche: {Head:1 Tail:{Head:2 Tail:{}}}switchLinkedList(mylist).(type){caseNil:fmt.Printf("%+v\n",Nil{})// n'est pas exécutécaseIntList:fmt.Printf("Tête: %v, Queue: %+v\n",mylist.Head,mylist.Tail)}// affiche: Tête: 1, Queue: {Head:2 Tail:{}}}
Ici j'ai défini un type pour les listes d'entiers : jusqu'à ce qu'on ait des types génériques en Go (dans un an ou deux), si on veut plus générique il faut utiliser interface{} à la place de int, ce qui donne des listes non limitées à un seul type, comme dans Python, Perl ou Lisp.
En pratique je sais pas si grand monde fait ceci, vu que les listes simplement chaînées n'apportent que rarement quelque chose par rapport à un tableau dynamique.
Même si l'on avait une représenation dynamique des types, je ne suis pas certain que l'on puisse réellement faire cela de manière générique et type safe.
Ah si, en Go c'est bien type safe : lorsque la valeur marshallée n'est plus compatible (parce que le code a évolué, par exemple, ou qu'on essaye de la récupérer dans le mauvais type), on obtient proprement une erreur.
Les types switchs sur les interfaces combinés au package reflect permettent d'inspecter les types (par exemples récupérer une liste des champs d'un struct). On peut donc définir printf récursivement sur une interface : les feuilles correspondent aux types de bases du langage (int, float64, etc.). Un exemple accessible est la définition récursive de Printf pour des types quelconques : ici on a un switch sur les types simples et un traitement différent pour les types plus complexes de type reflect.Value dans une fonction différent ici (en particulier la gestion récursives des structs, maps et tableaux).
Pour faire un printf type safe il me semble qu'il faut les types dépendants, et en OCaml c'est implémenté via un GADT.
Pour Printf en Go ce que je veux dire c'est qu'il peut afficher n'importe quel type de façon générique (le type est passé sous la forme interface{} pour pouvoir l'inspecter), même ceux définis par un utilisateur : pratique pour debugger par exemple. En OCaml il faut définir un affichage pour chaque type. En Haskell et Rust c'est pareil (aux derive près qui rendent la chose un peu plus facile, mais cela reste très manuel).
[^] # Re: Raisons d'essayer Rust
Posté par anaseto . En réponse au journal Retour d'expérience sur les langages de programmation. Évalué à 3.
C'est pas étonnant qu'ils soient absents, puisque la pratique de programmation qu'ils couvrent est déjà couverte par le concept des interfaces (bien que ce ne soient pas des type somme implémentés de façon scolaire). Je donne un exemple plus bas pour les listes simplement chaînées.
Uniquement à un type pointeur
*A(équivalent àoption A), et il n'y a pas une seule valeur nil, mais une différente pour chaque type pointeur (A(nil) est différent de B(nil)). Et c'est en effet un type somme qui couvre le cas courant du type option.Sauf que les conventions font qu'il n'y a pas 4 cas en pratique, mais bien 2 sauf 3 dans de rares cas comme Read qui peut renvoyer une erreur EOF qu'on peut vouloir traiter de façon particulière (cas non gérable par un simple type somme A + B de toutes façons).
Eh bien si, mais le fichier que j'ai donné contient uniquement la définition, pour les utilisations il faut regarder ailleurs comme ici qui implémente une marche sur l'ast en faisant un switch sur le type de l'interface qui sert à représenter un nœud.
Si on peut définir un ast, on peut définir en particulier une liste simplement chaînée :
Ici j'ai défini un type pour les listes d'entiers : jusqu'à ce qu'on ait des types génériques en Go (dans un an ou deux), si on veut plus générique il faut utiliser
interface{}à la place deint, ce qui donne des listes non limitées à un seul type, comme dans Python, Perl ou Lisp.En pratique je sais pas si grand monde fait ceci, vu que les listes simplement chaînées n'apportent que rarement quelque chose par rapport à un tableau dynamique.
Ah si, en Go c'est bien type safe : lorsque la valeur marshallée n'est plus compatible (parce que le code a évolué, par exemple, ou qu'on essaye de la récupérer dans le mauvais type), on obtient proprement une erreur.
Les types switchs sur les interfaces combinés au package reflect permettent d'inspecter les types (par exemples récupérer une liste des champs d'un struct). On peut donc définir printf récursivement sur une interface : les feuilles correspondent aux types de bases du langage (
int,float64, etc.). Un exemple accessible est la définition récursive de Printf pour des types quelconques : ici on a un switch sur les types simples et un traitement différent pour les types plus complexes de typereflect.Valuedans une fonction différent ici (en particulier la gestion récursives des structs, maps et tableaux).Pour Printf en Go ce que je veux dire c'est qu'il peut afficher n'importe quel type de façon générique (le type est passé sous la forme interface{} pour pouvoir l'inspecter), même ceux définis par un utilisateur : pratique pour debugger par exemple. En OCaml il faut définir un affichage pour chaque type. En Haskell et Rust c'est pareil (aux derive près qui rendent la chose un peu plus facile, mais cela reste très manuel).