← На главную

fmap работает с любым Functor, а List получает map из fold

02.08.2026 11:23 · hackernews

Объявляется трейт Functor[A]. Он параметризован типом A — типом элементов контейнера. Трейт требует, чтобы у реализующего типа был метод map[B](self, f: A->B) -> Self[B]. По сигнатуре видно, что метод принимает функцию из A в B и возвращает контейнер того же типа, но с элементами B.

Для типа List[A] написана функция свёртки fold[A,B]. Её параметры: список l, начальное значение z и функция f типа (B,A) -> B. Внутри используется сопоставление с образцом. Если список построен как Cons(h,t), функция вызывает List::fold(t, f(z,h), f) — сперва применяет f к накопленному значению z и голове h, затем рекурсивно обрабатывает хвост t. Если список пустой — Nil — возвращается начальное значение z. Это классическая левая свёртка.

На основе fold реализована функция sum(l: List[i32]) -> i32. В неё передаётся список целых чисел, начальное значение 0 и лямбда (a,b) => a+b. Каждый элемент по очереди прибавляется к аккумулятору, так что в итоге получается сумма всех элементов.

Дальше идёт реализация impl Functor[A] for List[A]. В ней метод map[B] определяется через fold. Начальное значение — Nil[B], то есть пустой список нужного типа. Шаг свёртки выглядит как (t,h) => Cons(f(h),t). Функция f применяется к голове h, затем полученное значение кладётся в новый узел Cons, а хвостом становится накопленный список t. Из-за левой свёртки и того, что каждый новый результат подставляется в голову, результирующий список получается развёрнутым относительно исходного. В коде этот момент не комментируется.

Затем определена функция fmap[A,B,F:Functor](f: A->B, x: F[A]) -> F[B]. Она принимает функцию и значение x любого типа, для которого реализован Functor. Внутри просто вызывается x.map(f). Это обобщённый способ применить функцию к содержимому контейнера, не зная конкретный тип контейнера.

Наконец, функция main имеет возвращаемый тип IO[Unit]. В ней создаётся список из трёх чисел: Cons(1, Cons(2, Cons(3, Nil))). Переменная l связывается с этим списком. Потом к нему применяется fmap(x => x*2, l), то есть каждый элемент умножается на 2. Результат сохраняется в l2. Дальше список l2 передаётся в List::sum, который складывает числа: 2, 4 и 6, получая 12. Строка int_to_str превращает число в строку, и print выводит её на экран.

Так трейт Functor даёт единый интерфейс для map, fold становится строительным блоком для других функций списка, а fmap позволяет обрабатывать любой контейнер, не привязываясь к его конкретному типу.

Читать оригинал →