He leído que Go en realidad no tiene una verdadera inferencia de tipos en el sentido que los lenguajes funcionales como ML o Haskell tienen, pero no he podido encontrar una comparación simple de entender de las dos versiones. ¿Podría alguien explicar en términos básicos cómo la inferencia de tipos en Go difiere de la inferencia de tipos en Haskell y las ventajas y desventajas de cada uno?
Vea esta respuesta de StackOverflow con respecto a la inferencia de tipos de Go. No estoy familiarizado con Go, pero según esta respuesta parece una "deducción de tipo" unidireccional (para tomar prestada alguna teminología de C ++). Significa que si tienes:
x := y + z
entonces el tipo de xse deduce descubriendo el tipo de y + z, que es algo relativamente trivial para el compilador. Para hacer esto, los tipos yy zdeben conocerse a priori : esto podría hacerse mediante anotaciones de tipo o inferirse de los literales asignados a ellos.
En contraste, la mayoría de los lenguajes funcionales tienen inferencia de tipo que usa toda la información posible dentro de un módulo (o función, si el algoritmo de inferencia es local) para derivar el tipo de las variables. Los algoritmos de inferencia complicados (como Hindley-Milner) a menudo involucran alguna forma de unificación de tipo (un poco como resolver ecuaciones) detrás de escena. Por ejemplo, en Haskell, si escribes:
let x = y + z
entonces Haskell puede inferir el tipo no solo xsino también yy zsimplemente basándose en el hecho de que está realizando una adición en ellos. En este caso:
x :: Num a => a
y :: Num a => a
z :: Num a => a
(La letra minúscula aaquí denota un tipo polimórfico , a menudo llamado "genéricos" en otros lenguajes como C ++. La Num a =>parte es una restricción para indicar que el asoporte de tipo tiene alguna noción de adición).
Aquí hay un ejemplo más interesante: el combinador de punto fijo que permite definir cualquier función recursiva:
let fix f = f (fix f)
Tenga en cuenta que en ninguna parte hemos especificado el tipo de f, ni hemos especificado el tipo de fix, sin embargo, el compilador de Haskell puede descubrir automáticamente que:
f :: t -> t
fix :: (t -> t) -> t
Esto dice que:
El parámetro fdebe ser una función de algún tipo arbitrario tal mismo tipo t.
fixes una función que recibe un parámetro de tipo t -> ty devuelve un resultado de tipo t.
más exactamente, Haskell puede decir que x, y, zson los mismos NumTipo de Eric, pero todavía puede ser Integers, Doubles, Ratio Integers ... Haskell está dispuesto a hacer una elección arbitraria entre tipos numéricos, pero no para otras clases de tipos.
John Dvorak
7
La inferencia de tipos en Go es extremadamente limitada y extremadamente simple. Funciona solo en una construcción de lenguaje (declaración de variable) y simplemente toma el tipo del lado derecho y lo usa como el tipo para la variable en el lado izquierdo.
La inferencia de tipos en Haskell se puede usar en todas partes, se puede usar para inferir los tipos para todo el programa. Se basa en la unificación, lo que significa que (conceptualmente) todos los tipos se infieren "a la vez" y todos pueden influenciarse entre sí: en Go, la información de tipo solo puede fluir desde el lado derecho de una declaración variable hacia la izquierda. lado de la mano, nunca en la otra dirección y nunca fuera de una declaración variable; en Haskell, la información de tipo fluye libremente en todas las direcciones a través de todo el programa.
Sin embargo, el sistema de tipos de Haskell es tan poderoso que la inferencia de tipos puede fallar en inferir un tipo (o más precisamente: se deben establecer restricciones para que siempre se pueda inferir un tipo). El sistema de tipos de Go es tan simple (sin subtipo, sin polimorfismo paramétrico) y su inferencia es tan limitada que siempre tiene éxito.
"en Haskell, la información de tipo fluye libremente en todas las direcciones a través de todo el programa": encuentro que esto da una muy buena intuición. +1
Giorgio
Las afirmaciones que hace esta respuesta en el último párrafo son un poco engañosas. Haskell no tiene subtipo. Además, el polimorfismo paramétrico no causa ningún problema para la integridad de la inferencia de tipos: Hindley-Milner en el cálculo lambda polimórfico siempre encuentra el tipo más general. Haskell puede fallar en inferir tipos, pero esto será en características sofisticadas del sistema de tipos como GADTs donde, cuando se formula ingenuamente, no existe un tipo principal (es decir, "mejor opción").
x
,y
,z
son los mismosNum
Tipo de Eric, pero todavía puede serInteger
s,Double
s,Ratio Integer
s ... Haskell está dispuesto a hacer una elección arbitraria entre tipos numéricos, pero no para otras clases de tipos.La inferencia de tipos en Go es extremadamente limitada y extremadamente simple. Funciona solo en una construcción de lenguaje (declaración de variable) y simplemente toma el tipo del lado derecho y lo usa como el tipo para la variable en el lado izquierdo.
La inferencia de tipos en Haskell se puede usar en todas partes, se puede usar para inferir los tipos para todo el programa. Se basa en la unificación, lo que significa que (conceptualmente) todos los tipos se infieren "a la vez" y todos pueden influenciarse entre sí: en Go, la información de tipo solo puede fluir desde el lado derecho de una declaración variable hacia la izquierda. lado de la mano, nunca en la otra dirección y nunca fuera de una declaración variable; en Haskell, la información de tipo fluye libremente en todas las direcciones a través de todo el programa.
Sin embargo, el sistema de tipos de Haskell es tan poderoso que la inferencia de tipos puede fallar en inferir un tipo (o más precisamente: se deben establecer restricciones para que siempre se pueda inferir un tipo). El sistema de tipos de Go es tan simple (sin subtipo, sin polimorfismo paramétrico) y su inferencia es tan limitada que siempre tiene éxito.
fuente