ci sono un paio di concetti è necessario capire a dare un senso a questa firma tipo e non so quali già fate, quindi ho fatto del mio meglio per spiegare ogni concetto importante:
currying
Come sapete, se si ha il tipo foo -> bar
, questo descrive una funzione prendendo un argomento di tipo foo
e restituendo un risultato di tipo bar
. Poiché ->
è associativo corretto, il tipo foo -> bar -> baz
corrisponde a foo -> (bar -> baz)
e descrive quindi una funzione che accetta un argomento di tipo foo
e restituisce un valore di tipo bar -> baz
, che significa che il valore di ritorno è una funzione che assume un valore di tipo bar
e restituisce un valore di tipo baz
.
Tale funzione può essere chiamata come my_function my_foo my_bar
, che a causa dell'applicazione funzione viene lasciata associativa, è la stessa (my_function my_foo) my_bar
, cioè si applica my_function
all'argomento my_foo
e poi applica la funzione che viene restituito come risultato all'argomento my_bar
.
Perché può essere chiamato in questo modo, una funzione di tipo foo -> bar -> baz
viene spesso chiamata "una funzione che accetta due argomenti" e lo farò nel resto di questa risposta.
variabili di tipo
Se si definisce una funzione come let f x = x
, avrà il tipo 'a -> 'a
. Ma 'a
non è in realtà un tipo definito in nessuna parte della libreria standard OCaml, quindi che cos'è?
Qualsiasi tipo che inizia con un '
è una variabile di tipo . Una variabile di tipo può rappresentare qualsiasi tipo possibile. Quindi nell'esempio sopra f
può essere chiamato con uno int
o uno string
o uno list
o qualcosa del genere - non importa.
Inoltre, se la stessa variabile di tipo appare in una segnatura di tipo più di una volta, sarà valida per lo stesso tipo. Quindi nell'esempio sopra ciò significa che il tipo restituito di f
è uguale al tipo di argomento. Quindi, se f
viene chiamato con uno int
, restituisce uno int
. Se viene chiamato con un string
, restituisce un string
e così via.
Quindi una funzione di tipo 'a -> 'b -> 'a
potrebbe richiedere due argomenti di qualsiasi tipo (che potrebbero non essere dello stesso tipo per il primo e il secondo argomento) e restituisce un valore dello stesso tipo del primo argomento, mentre una funzione di tipo 'a -> 'a -> 'a
prenderà due argomenti dello stesso tipo.
Una nota circa l'inferenza del tipo: a meno che non si attribuisca esplicitamente a una funzione una firma del tipo, OCaml dedurrà sempre il tipo più generale possibile. Quindi, a meno che una funzione non usi operazioni che funzionano solo con un determinato tipo (ad esempio +
), il tipo dedotto conterrà le variabili di tipo.
Ora per spiegare il tipo di ...
val something : ('a -> 'b -> 'c) -> ('a -> 'd -> 'b) -> 'a -> 'd -> 'c = <fun>
Questa firma tipo vi dice che something
è una funzione di prendere quattro argomenti.
Il tipo del primo argomento è 'a -> 'b -> 'c
. Cioè una funzione che prende due argomenti di tipi arbitrari e possibilmente diversi e restituisce un valore di un tipo arbitrario.
Il tipo del secondo argomento è 'a -> 'd -> 'b
. Questa è di nuovo una funzione con due argomenti. La cosa importante da notare qui è che il primo argomento della funzione deve avere lo stesso tipo del primo argomento della prima funzione e il valore di ritorno della funzione deve avere lo stesso tipo del secondo argomento della prima funzione.
Il tipo del terzo argomento è 'a
, che è anche il tipo dei primi argomenti di entrambe le funzioni.
Infine, il tipo del quarto argomento è 'd
, che è il tipo del secondo argomento della seconda funzione.
Il valore restituito sarà di tipo 'c
, ovvero il tipo di ritorno della prima funzione.
Nota di terminologia (che potrebbe aiutare le ricerche nella letteratura): "firme" in Ocaml di solito significano qualcos'altro, vale a dire un analogo di tipi ma per moduli piuttosto che espressioni e valori di base. Quello che stai chiedendo è talvolta chiamato "firma del tipo" ma spesso solo "tipo" o "schema tipo" quando ci sono variabili. – Gilles