Подводя итог вашему замечанию о факториалах, мы можем отметить, что натуральные числа можно рассматривать как рекурсивную структуру данных:
data Nat = Zero | Succ Nat
В терминах рекурсивных схем машинного оборудования соответствующий базовый функтор будет:
data NatF a = ZeroF | SuccF a
deriving (Functor)
NatF
, однако, изоморфен Maybe
. Таким образом, рекурсивные схемы удобно делают Maybe
базовым функтором типа Natural
из base . Например, вот тип ana
, специализированный для Natural
:
ana @Natural :: (a -> Maybe a) -> a -> Natural
Мы можем использовать его, чтобы написать раскрывающуюся личность для Natural
:
{-# LANGUAGE LambdaCase #-}
import Numeric.Natural
import Data.Functor.Foldable
idNatAna :: Natural -> Natural
idNatAna = ana $ \case
0 -> Nothing
x -> Just (x - 1)
Коалгебра, которую мы только что дали ana
, равна project
для Natural
, project
- функция, которая разворачивает один слой рекурсивной структуры. В терминах рекурсивных схем словарь, ana project
- это раскрытие идентичности, а cata embed
- это складка идентичности. (В частности, project
для списков равно uncons
из Data.List
, за исключением того, что оно кодируется ListF
вместо Maybe
.)
Кстати, факториальная функция может быть выражена как параморфизм натуралов (, как указано в примечании в конце этого вопроса ). Мы также можем реализовать это в терминах рекурсивных схем :
fact :: Natural -> Natural
fact = para $ \case
Nothing -> 1
Just (predec, prod) -> prod * (predec + 1)
para
делает доступным на каждом рекурсивном шаге оставшуюся часть структуры, которую нужно сложить (если бы мы складывали список, это был бы его хвост). В данном случае я назвал предоставленное значение predec
, потому что на n
-ом рекурсивном шаге снизу вверх predec
равно n - 1
.
Обратите внимание, что hylomorphism user11228628 , вероятно, является более эффективной реализацией, если вам случится позаботиться об этом. (Я не тестировал их, хотя.)