Comprensione dell'errore di tipo: & # 8220; firma prevista Int * Int- > Int ma ottenuta Int * Int- > Int & # 8221;

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

  •  11-07-2019
  •  | 
  •  

Domanda

I commenti su Steve Yegge ' post about Javascript lato server ha iniziato a discutere i meriti dei sistemi di tipo nelle lingue e questo commento descrive:

  

... esempi da HM in cui puoi ottenere cose come:

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

Puoi dare un esempio di una definizione di funzione (o due?) e una chiamata di funzione che produrrebbe quell'errore? Sembra che potrebbe essere abbastanza difficile eseguire il debug in un programma di grandi dimensioni.

Inoltre, avrei potuto riscontrare un errore simile in Miranda ? (Non lo uso da 15 anni e quindi il mio ricordo è vago)

È stato utile?

Soluzione

Prenderei le opinioni di Yegge (e Ola Bini) sulla tipizzazione statica con un granello di sale. Se apprezzi ciò che ti dà la tipizzazione statica, imparerai come funziona il sistema di tipi del linguaggio di programmazione che scegli.

IIRC, ML utilizza la sintassi '*' per le tuple. & Lt; tipo > * < digita > è un tipo di tupla con due elementi. Quindi, (1, 2) avrebbe int * int type.

Sia Haskell che ML usano - > per funzioni. In ML, int * int - > int sarebbe il tipo di una funzione che accetta una tupla di int e int e la mappa a un int.

Uno dei motivi per cui potresti vedere un errore che assomiglia vagamente a quello citato da Ola quando arrivi in ??ML da una lingua diversa, è se provi a passare argomenti usando parentesi e virgole, come si farebbe in C o Pascal, a una funzione che accetta due parametri.

Il problema è che i linguaggi funzionali generalmente modellano le funzioni di più di un parametro come funzioni che restituiscono funzioni; tutte le funzioni accettano solo un singolo argomento. Se la funzione deve accettare due argomenti, accetta invece un argomento e restituisce una funzione di un singolo argomento, che restituisce il risultato finale e così via. Per rendere tutto ciò leggibile, l'applicazione della funzione viene eseguita semplicemente mediante una congiunzione (ovvero posizionando le espressioni una accanto all'altra).

Quindi, una semplice funzione in ML (nota: sto usando F # come il mio ML) potrebbe apparire un po 'come:

let f x y = x + y;;

Ha tipo:

val f : int -> int -> int

(Una funzione che accetta un numero intero e restituisce una funzione che a sua volta accetta un numero intero e restituisce un numero intero.)

Tuttavia, se lo chiami ingenuamente con una tupla:

f(1, 2)

... otterrai un errore, perché hai passato un int * int a qualcosa che si aspetta un int.

Mi aspetto che questo sia il "problema" Ola stava cercando di lanciare aspersioni su. Tuttavia, non penso che il problema sia grave come pensa; certamente, è molto peggio nei modelli C ++.

Altri suggerimenti

È possibile che questo fosse in riferimento a un compilatore scritto male che non riusciva a inserire parentesi per chiarire i messaggi di errore. In particolare, la funzione prevedeva una tupla di int e ha restituito un int , ma hai passato una tupla di int e una funzione da int in int . Più concretamente (in ML):

fun f g = g (1, 2);

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

Questo produrrà un errore di tipo simile al seguente:

  

Tipo previsto int * int - > int , ho ottenuto il tipo int * (int - > int)

Se le parentesi vengono omesse, questo errore può essere fastidiosamente ambiguo.

Vale la pena notare che questo problema è lungi dall'essere specifico di Hindley-Milner. In effetti, non riesco a pensare a nessun tipo strano errori specifici di H-M. Almeno, nessuno come l'esempio fornito. Sospetto che Ola stesse solo soffiando fumo.

Poiché molti linguaggi funzionali ti consentono di ricollegare i nomi dei tipi nello stesso modo in cui puoi ricollegare le variabili, in realtà è abbastanza facile finire con un errore come questo, soprattutto se usi nomi piuttosto generici per i tuoi tipi (ad es. t ) in diversi moduli. Ecco un semplice esempio in 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

Quello che ho fatto qui è ricollegare l'identificatore di tipo int a un nuovo tipo incompatibile con il tipo int incorporato. Con un po 'più di sforzo, possiamo ottenere più o meno lo stesso errore di cui sopra:

# 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
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top