Считается, что если два значения определены полностью, то мы не можем сказать какое из них информатив-
нее. Так к примеру для логических значений мы не можем сказать какое значение более определено
или
Рассмотрим пример по-сложнее. Частично определённые значения:
Индуктивные и коиндуктивные типы | 245
если
Если вспомнить как происходит вычисление значения, то значение
ное значение
екты это множества с частичным порядком. Что означает требование монотонности функции?
Монотонность в контексте операции
говорит о том, что чем больше определён вход функции тем больше
определён выход:
Это требование накладывает запрет на возможность проведения сопоставления с образцом по значению
isBot :: Bool -> Bool
isBot undefined = True
isBot _
=
undefinedПолнота частично упорядоченного множества означает, что у любой последовательности
есть значение
полные частично упорядоченные множества мы разобрались. А что такое полиномиальный функтор?
Полиномиальный функтор
Полиномиальный функтор – это функтор который построен лишь с помощью операций суммы, произве-
дения, постоянных функторов, тождественного фуктора и композиции функторов. Определим эти операции:
• Сумма функторов
(
• Произведение функторов
(
• Постоянный функтор отображает все объекты категории в один объект, а стрелки в тождественнубю
стрелку этого объекта, мы будем обозначать постоянный функтор подчёркиванием:
=
=
• Тождественный функтор оставляет объекты и стрелки неизменными:
=
=
• Композиция функторов
246 | Глава 16: Категориальные типы
По определению функции построенные с помощью этих операций называют полиномиальными. Опреде-
лим несколько типов данных с помощью полиномиальных функторов. Определим логические значения:
Объект 1 обозначает любую константу, это конечный объект исходной категории. Нам не важны имена
конструкторов, но важна структура типа.
Определим натуральные числа:
Эта запись обозначает начальный объект для
ление списка:
Список это начальный объект
Определим потоки:
Потоки являются конечным объектом
16.3 Гиломорфизм
Оказывается, что с помощью катаморфизма и анаморфизма мы можем определить функцию fix, то есть
мы можем выразить любую рекурсивную функцию с помощью структурной рекурсии.
Функция fix строит бесконечную последовательность применений некоторой функции f.
f (f (f ...
)))Сначала с помощью анаморфизма мы построим бесконечный список, который содержит функцию f во
всех элементах:
repeat f =
f : f : f : ...А затем заменим конструктор :
на применение. В итоге мы получим такую функцию:fix ::
(a -> a) -> afix =
foldr ($) undefined . repeatУбедимся, что эта функция работает:
Prelude> let
fix = foldr ($) undefined . repeatPrelude>
take 3 $ y (1:)[1,1,1]
Prelude>
fix (\f n -> if n==0 then 0 else n + f (n-1)) 1055
Теперь давайте определим функцию fix через функции cata и ana:
fix ::
(a -> a) -> afix =
cata (\(Cons f a) -> f a) . ana (\a -> Cons a a)Эта связка анаморфизм с последующим катаморфизмом встречается так часто, что ей дали специальное
имя.
hylo :: Functor
f => (f b -> b) -> (a -> f a) -> (a -> b)hylo phi psi =
cata phi . ana psiОтметим, что эту функцию можно выразить и по-другому:
Гиломорфизм | 247
hylo :: Functor
f => (f b -> b) -> (a -> f a) -> (a -> b)hylo phi psi =
phi . (fmap $ hylo phi psi) . psiЭтот вариант более эффективен по расходу памяти, мы не строим промежуточное значение Fix
f, а сразуобрабатываем значения в функции phi по ходу их построения в функции psi. Давайте введём инфиксную
операцию гиломорфизм для этого определения:
(>>
) :: Functor f => (a -> f a) -> (f b -> b) -> (a -> b)psi >>
phi = phi . (fmap $ hylo phi psi) . psiТеперь давайте скроем одноимённую функцию из Prelude
и определим несколько рекурсивных функцийс помощью гиломорфизма. Начнём с функции вычисления суммы чисел от нуля до данного числа:
sumInt :: Int -> Int