Comme le compilateur points, il n'y a pas de type qui peut être attribué x
de sorte que l'expression (x x)
est bien typés (ce n'est pas strictement vrai; vous pouvez taper explicitement x
comme obj->_
- voir mon dernier paragraphe). Vous pouvez contourner ce problème en déclarant récursive de type, de sorte qu'une expression très semblable:
type 'a Rec = Rec of ('a Rec -> 'a)
Maintenant, le Y combinator peut être écrite comme:
let y f =
let f' (Rec x as rx) = f (x rx)
f' (Rec f')
Malheureusement, vous trouverez que ce n'est pas très utile car F# est un langage "strict",
de sorte que toute fonction que vous essayez de définir l'utilisation de ce combinateur va provoquer un débordement de pile.
Au lieu de cela, vous devez utiliser l'applicatif version du 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')
Une autre option serait d'utiliser explicitement la paresse de définir la normale afin de 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
Ceci a l'inconvénient que la fonction récursive définitions maintenant besoin d'un explicite de la force de la paresse de la valeur (à l'aide de l' Value
de la propriété):
let factorial = y (fun f -> function | 0 -> 1 | n -> n * (f.Value (n - 1)))
Cependant, il a l'avantage que vous pouvez définir des non-fonction récursive des valeurs, comme vous le feriez dans un paresseux langue:
let ones = y (fun ones -> LazyList.consf 1 (fun () -> ones.Value))
Comme dernière alternative, vous pouvez essayer de mieux rapprocher les non typée lambda calcul en utilisant la boxe et de passer. Ce serait vous donner (encore une fois à l'aide de l'applicatif version du Y combinator):
let y f =
let f' (x:obj -> _) = f (fun y -> x x y)
f' (fun x -> f' (x :?> _))
Cela a le désavantage évident qu'il entraîne boxing et unboxing, mais au moins c'est purement interne à la mise en œuvre et ne sera jamais réellement conduire à l'échec lors de l'exécution.