In OCaml, è legale avere in .mli
:η-espansione in un linguaggio funzionale puro
val f : 'a -> 'a
val g : 'a -> 'a
e .ml
:
let f x = x
let g = f
Eppure in F #, questa viene rifiutata:
eta_expand.ml(2,5): error FS0034: Module 'Eta_expand' contains
val g : ('a -> 'a)
but its signature specifies
val g : 'a -> 'a
The arities in the signature and implementation differ. The signature specifies that 'g' is function definition or lambda expression accepting at least 1 argument(s), but the implementation is a computed function value. To declare that a computed function value is a permitted implementation simply parenthesize its type in the signature, e.g.
val g: int -> (int -> int)
instead of
val g: int -> int -> int.
Una soluzione è quella di η-espandere la definizione di g:
let g x = f x
Se il mio codice è puramente funzionale (senza eccezioni, senza effetti collaterali, ecc) questo dovrebbe essere equivalente (in realtà, potrebbe essere ancora migliore rispetto al polimorfismo, a seconda di come il linguaggio generalizza tipi: in OCaml le applicazioni parziali non producono funzioni polimorfiche, ma la loro espansione η lo fa).
Esiste qualche svantaggio nell'espansione η sistematica?
Due risposte eludono la domanda su η-expansion :-) e suggeriscono invece di aggiungere parentesi attorno al mio tipo funzionale. Questo perché, a quanto pare, F # si distingue a livello di digitazione tra la definizione "vera" di funzioni (come espressioni λ e definizioni calcolate, come nelle applicazioni parziali); presumibilmente perché le espressioni λ si mappano direttamente alle funzioni CLR mentre le definizioni calcolate mappano per delegare oggetti. (Non sono sicuro di questa interpretazione e apprezzerei se qualcuno molto familiare con F # potrebbe puntare a documenti di riferimento che descrivono questo.)
Una soluzione sarebbe quella di aggiungere sistematicamente parentesi a tutti i tipi di funzione nel .mli
, ma Temo che questo potrebbe portare a inefficienze. Un altro sarebbe quello di rilevare le funzioni calcolate e aggiungere parentesi i tipi corrispondenti nel .mli
. Una terza soluzione sarebbe quella di η-espandere i casi più ovvi e scegliere tra parentesi gli altri.
Non ho dimestichezza con gli interni F #/CLR per misurare quali sono le prestazioni significative o le penalità di interfaccia.
Basta renderlo 'val g: ('a ->' a)'. È una funzionalità/bug nota del sistema di tipo F #. –
Ancora "basta fare" - presumibilmente se le espressioni λ e le funzioni calcolate non hanno tipi intercambiabili, è per una buona ragione. Inoltre, questo è un codice generato automaticamente, quindi la questione è un po 'più complicata della semplice aggiunta di alcune parentesi manualmente ... –
C'è una differenza tra i due in questo caso, uno è compilato in un metodo e l'altro in uno statico ' Func 'proprietà. La compatibilità non è bidirezionale (ovvero, 'a -> b: <: (a -> b)', ma non '(a -> b): <: a -> b'). –