Come il compilatore fa notare, non v'è alcun tipo che può essere assegnato a x
in modo che l'espressione (x x)
è ben tipizzato-(questo non è del tutto vero, è possibile digitare esplicitamente x
come obj->_
- vedere il mio ultimo paragrafo) . È possibile aggirare il problema dichiarando un tipo ricorsivo in modo che un'espressione molto simile funzionerà:
type 'a Rec = Rec of ('a Rec -> 'a)
Ora la Y-Combinator può essere scritta come:
let y f =
let f' (Rec x as rx) = f (x rx)
f' (Rec f')
Purtroppo, troverete questo non è molto utile perché F # è un linguaggio rigoroso, , quindi qualsiasi funzione che si tenta di definire utilizzando questo combinatore causerà un sovraccarico dello stack. Invece, è necessario utilizzare la versione applicativa di ordine inferiore del Y-Combinator (\f.(\x.f(\y.(x x)y))(\x.f(\y.(x x)y))
):
let y f =
let f' (Rec x as rx) = f (fun y -> x rx y)
f' (Rec f')
Un'altra opzione sarebbe quella di utilizzare la pigrizia esplicito per definire il normale ordine Y-Combinator:
type 'a Rec = Rec of ('a Rec -> 'a Lazy)
let y f =
let f' (Rec x as rx) = lazy f (x rx)
(f' (Rec f')).Value
Questo ha lo svantaggio che le definizioni di funzioni ricorsive devono ora una forza esplicita del valore pigro (utilizzando la proprietà Value
):
let factorial = y (fun f -> function | 0 -> 1 | n -> n * (f.Value (n - 1)))
Tuttavia, ha il vantaggio che è possibile definire i valori ricorsive non funzione, proprio come si potrebbe in un linguaggio pigro:
let ones = y (fun ones -> LazyList.consf 1 (fun() -> ones.Value))
Come alternativa finale, si può provare a approssimare meglio il lambda calcolo tipizzato dal usando il pugilato e il downcasting. Questo darebbe (sempre utilizzando la versione applicativa di ordine inferiore del Y-Combinator):
let y f =
let f' (x:obj -> _) = f (fun y -> x x y)
f' (fun x -> f' (x :?> _))
Questo ha lo svantaggio ovvio che causerà la boxe non necessari e unboxing, ma almeno questo è del tutto interno alla realizzazione e in realtà non porterà mai a un errore in fase di runtime.
* Ma Juliet! Il tuo y-combinator funziona bene nel modo in cui è stato scritto, perché ti interessa che sia scritto con "let rec?" * Solo perché. O davvero, solo per vedere come è fatto, non perché ne ho bisogno. Un altro utente SO mi ha chiesto come è stato scritto senza 'let rec', e non sono riuscito a capirlo subito. – Juliet