Comprensión del error de tipo: "firma esperada Int * Int- > Int pero obtuve Int * Int- > Int"

StackOverflow https://stackoverflow.com/questions/324612

  •  11-07-2019
  •  | 
  •  

Pregunta

Los comentarios sobre Steve Yegge 's publicar sobre Javascript del lado del servidor comenzó a discutir los méritos de los sistemas de tipos en idiomas y esto comentario describe:

  

... ejemplos de HM sistemas de estilo donde puede obtener cosas como:

expected signature Int*Int->Int but got Int*Int->Int

¿Puede dar un ejemplo de una definición de función (o dos) y una llamada a función que produciría ese error? Parece que podría ser bastante difícil de depurar en un programa de gran tamaño.

Además, ¿podría haber visto un error similar en Miranda ? (No lo he usado en 15 años, por lo que mi recuerdo es vago)

¿Fue útil?

Solución

Tomaría las opiniones de Yegge (y Ola Bini) sobre la escritura estática con un grano de sal. Si aprecia lo que le ofrece la escritura estática, aprenderá cómo funciona el sistema de tipos del lenguaje de programación que elija.

IIRC, ML utiliza la sintaxis '*' para las tuplas. < escriba > * < tipo > Es un tipo de tupla con dos elementos. Entonces, (1, 2) tendría el tipo int * int.

Tanto Haskell como ML usan - > para funciones. En ML, int * int - > int sería el tipo de función que toma una tupla de int e int y la asigna a un int.

Una de las razones por las que puede ver un error que se parece vagamente al que Ola citó al ingresar a ML desde un idioma diferente, es si intenta pasar argumentos usando paréntesis y comas, como lo haría en C o Pascal, a una función que toma dos parámetros.

El problema es que los lenguajes funcionales generalmente modelan funciones de más de un parámetro como funciones que devuelven funciones; Todas las funciones solo toman un único argumento. Si la función debe tomar dos argumentos, toma un argumento y devuelve una función de un solo argumento, que devuelve el resultado final, y así sucesivamente. Para que todo esto sea legible, la aplicación de funciones se realiza simplemente por conjunción (es decir, colocando las expresiones una al lado de la otra).

Entonces, una función simple en ML (nota: estoy usando F # como mi ML) podría verse un poco como:

let f x y = x + y;;

Tiene tipo:

val f : int -> int -> int

(Una función que toma un entero y devuelve una función que a su vez toma un entero y devuelve un entero).

Sin embargo, si ingenuamente lo llamas con una tupla:

f(1, 2)

... obtendrá un error, porque pasó un int * int a algo que espera un int.

Espero que este sea el " problema " Ola estaba tratando de lanzar aspersiones a. Sin embargo, no creo que el problema sea tan grave como él piensa; ciertamente, es mucho peor en las plantillas de C ++.

Otros consejos

Es posible que esto fuera en referencia a un compilador mal escrito que no pudo insertar paréntesis para desambiguar mensajes de error. Específicamente, la función esperaba una tupla de int y devolvió un int , pero pasó una tupla de int y una función de int a int . Más concretamente (en ML):

fun f g = g (1, 2);

f (42, fn x => x * 2)

Esto producirá un error de tipo similar al siguiente:

  

Tipo esperado int * int - > int , obtuve el tipo int * (int - > int)

Si se omiten los paréntesis, este error puede ser molestamente ambiguo.

Vale la pena señalar que este problema está lejos de ser específico de Hindley-Milner. De hecho, no puedo pensar en ningún tipo extraño de errores que sean específicos de H-M. Al menos, ninguno como el ejemplo dado. Sospecho que Ola solo estaba echando humo.

Dado que muchos lenguajes funcionales le permiten volver a vincular nombres de tipo de la misma manera que puede volver a vincular variables, en realidad es bastante fácil terminar con un error como este, especialmente si usa nombres algo genéricos para sus tipos (por ejemplo, t ) en diferentes módulos. Aquí hay un ejemplo simple en OCaml:

# let f x = x + 1;;
val f : int -> int = <fun>
# type int = Foo of string;;
type int = Foo of string
# f (Foo "hello");;
This expression has type int but is here used with type int

Lo que he hecho aquí es volver a vincular el identificador de tipo int a un nuevo tipo que sea incompatible con el tipo incorporado int . Con un poco más de esfuerzo, podemos obtener más o menos el mismo error que el anterior:

# let f g x y = g(x,y) + x + y;;
val f : (int * int -> int) -> int -> int -> int = <fun>
# type int = Foo of int;;
type int = Foo of int
# let h (Foo a, Foo b) = (Foo a);;
val h : int * int -> int = <fun>
# f h;;
This expression has type int * int -> int but is here used with type
  int * int -> int
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top