Jednym z kluczowych powodów, dla których jawne jest użycie, rec
ma związek z wnioskiem typu Hindleya-Milnera, który leży u podstaw wszystkich funkcjonalnych języków programowania ze statycznym typowaniem (aczkolwiek zmienionych i rozszerzonych na różne sposoby).
Jeśli masz definicję let f x = x
, spodziewasz się, że będzie miała typ 'a -> 'a
i będzie miała zastosowanie do różnych 'a
typów w różnych punktach. Ale równie dobrze, jeśli napiszesz let g x = (x + 1) + ...
, spodziewasz się, że x
będziesz traktowany jak int
w pozostałej części treści g
.
Sposób, w jaki wnioskowanie Hindleya-Milnera radzi sobie z tym rozróżnieniem, prowadzi przez wyraźny krok uogólnienia . W pewnych momentach podczas przetwarzania twojego programu, system typów zatrzymuje się i mówi "ok, typy tych definicji zostaną uogólnione w tym miejscu, tak że gdy ktoś ich użyje, wszelkie wolne zmienne typu w ich typie będą świeżo utworzone, a zatem nie będzie kolidować z innymi zastosowaniami tej definicji. ”
Okazuje się, że sensownym miejscem do zrobienia tego uogólnienia jest sprawdzenie wzajemnie rekurencyjnego zestawu funkcji. Jakikolwiek wcześniejszy, a będziesz uogólniać zbyt wiele, co prowadzi do sytuacji, w których typy mogą faktycznie kolidować. Później za mało uogólniasz, tworząc definicje, których nie można używać z wystąpieniami wielu typów.
A zatem, biorąc pod uwagę, że narzędzie do sprawdzania typów musi wiedzieć, które zestawy definicji są wzajemnie rekurencyjne, co może zrobić? Jedną z możliwości jest po prostu przeprowadzenie analizy zależności na wszystkich definicjach w zakresie i uporządkowanie ich w jak najmniejszych grupach. Haskell faktycznie to robi, ale w językach takich jak F # (oraz OCaml i SML), które mają nieograniczone skutki uboczne, jest to zły pomysł, ponieważ może również zmienić kolejność efektów ubocznych. Zamiast tego prosi użytkownika o wyraźne zaznaczenie, które definicje są wzajemnie rekurencyjne, a zatem, przez rozszerzenie, gdzie powinno nastąpić uogólnienie.