J'ai lu que Go n'a pas vraiment d'inférence de type dans le sens où les langages fonctionnels tels que ML ou Haskell l'ont fait, mais je n'ai pas pu trouver une comparaison simple à comprendre des deux versions. Quelqu'un pourrait-il expliquer en termes de base en quoi l'inférence de type dans Go diffère de l'inférence de type dans Haskell, et les avantages / inconvénients de chacun?
Voir cette réponse StackOverflow concernant l'inférence de type de Go. Je ne suis pas familier avec Go moi-même, mais sur la base de cette réponse, cela semble être une "déduction de type" à sens unique (pour emprunter de la téminologie C ++). Cela signifie que si vous avez:
x := y + z
puis le type de xest déduit en déterminant le type de y + z, ce qui est une chose relativement banale à faire pour le compilateur. Pour ce faire, les types de yet zdoivent être connus a priori : cela peut être fait via des annotations de type ou déduit des littéraux qui leur sont attribués.
En revanche, la plupart des langages fonctionnels ont une inférence de type qui utilise toutes les informations possibles dans un module (ou une fonction, si l'algorithme d'inférence est local) pour dériver le type des variables. Les algorithmes d'inférence compliqués (tels que Hindley-Milner) impliquent souvent une certaine forme d' unification de type (un peu comme la résolution d'équations) dans les coulisses. Par exemple, dans Haskell, si vous écrivez:
let x = y + z
Haskell peut alors déduire le type non seulement xmais aussi yet zsimplement en se basant sur le fait que vous effectuez des ajouts sur eux. Dans ce cas:
x :: Num a => a
y :: Num a => a
z :: Num a => a
(Les minuscules adésignent ici un type polymorphe , souvent appelé «génériques» dans d'autres langages comme C ++. La Num a =>partie est une contrainte pour indiquer que le asupport de type a une certaine notion d'addition.)
Voici un exemple plus intéressant: le combinateur à virgule fixe qui permet de définir n'importe quelle fonction récursive:
let fix f = f (fix f)
Notez que nulle part nous n'avons spécifié le type de f, ni nous n'avons spécifié le type de fix, pourtant le compilateur Haskell peut automatiquement comprendre que:
f :: t -> t
fix :: (t -> t) -> t
Cela dit que:
Le paramètre fdoit être une fonction d'un type arbitraire tau même type t.
fixest une fonction qui reçoit un paramètre de type t -> tet renvoie un résultat de type t.
plus exactement, Haskell peut dire que x, y, zsont les mêmes Numtypes eric, mais ils peuvent encore Integers, Doubles, Ratio Integers ... Haskell est prêt à faire un choix arbitraire entre les types numériques, mais pas pour d' autres classes de types.
John Dvorak
7
L'inférence de type dans Go est extrêmement limitée et extrêmement simple. Il ne fonctionne que dans une seule construction de langage (déclaration de variable) et prend simplement le type du côté droit et l'utilise comme type pour la variable du côté gauche.
L'inférence de type dans Haskell peut être utilisée partout, elle peut être utilisée pour déduire les types pour l'ensemble du programme. Il est basé sur l'unification, ce qui signifie que (conceptuellement) tous les types sont déduits "à la fois" et ils peuvent tous s'influencer les uns les autres: dans Go, les informations de type ne peuvent circuler que du côté droit d'une déclaration de variable vers la gauche- côté, jamais dans l'autre sens et jamais en dehors d'une déclaration de variable; dans Haskell, les informations de type circulent librement dans toutes les directions tout au long du programme.
Cependant, le système de types de Haskell est si puissant que l'inférence de type peut en fait échouer à déduire un type (ou plus précisément: des restrictions doivent être mises en place pour qu'un type puisse toujours être déduit). Le système de types de Go est si simple (pas de sous-typage, pas de polymorphisme paramétrique) et son inférence est si limité qu'il réussit toujours.
"dans Haskell, les informations de type circulent librement dans toutes les directions à travers tout le programme": je trouve que cela donne une très bonne intuition. +1
Giorgio
Les affirmations que cette réponse fait dans le dernier paragraphe sont un peu trompeuses. Haskell n'a pas de sous-typage. De plus, le polymorphisme paramétrique ne pose aucun problème pour l'exhaustivité de l'inférence de type: Hindley-Milner sur le calcul lambda polymorphe trouve toujours le type le plus général. Haskell peut ne pas déduire les types, mais ce sera sur des fonctionnalités de système de type sophistiquées comme les GADT où, lorsqu'il est formulé naïvement, aucun type principal (c'est-à-dire "meilleur choix") n'existe.
x
,y
,z
sont les mêmesNum
types eric, mais ils peuvent encoreInteger
s,Double
s,Ratio Integer
s ... Haskell est prêt à faire un choix arbitraire entre les types numériques, mais pas pour d' autres classes de types.L'inférence de type dans Go est extrêmement limitée et extrêmement simple. Il ne fonctionne que dans une seule construction de langage (déclaration de variable) et prend simplement le type du côté droit et l'utilise comme type pour la variable du côté gauche.
L'inférence de type dans Haskell peut être utilisée partout, elle peut être utilisée pour déduire les types pour l'ensemble du programme. Il est basé sur l'unification, ce qui signifie que (conceptuellement) tous les types sont déduits "à la fois" et ils peuvent tous s'influencer les uns les autres: dans Go, les informations de type ne peuvent circuler que du côté droit d'une déclaration de variable vers la gauche- côté, jamais dans l'autre sens et jamais en dehors d'une déclaration de variable; dans Haskell, les informations de type circulent librement dans toutes les directions tout au long du programme.
Cependant, le système de types de Haskell est si puissant que l'inférence de type peut en fait échouer à déduire un type (ou plus précisément: des restrictions doivent être mises en place pour qu'un type puisse toujours être déduit). Le système de types de Go est si simple (pas de sous-typage, pas de polymorphisme paramétrique) et son inférence est si limité qu'il réussit toujours.
la source