В общем смысле, контейнер или контекст - это абстракция, которая оборачивает или содержит некоторые данные, предоставляя интерфейс для работы с ними.
Пусть у нас есть некоторое значение, допустим, число “10”. Мы можем спокойно применить к нему функцию, допустим “(+)2)”:
> (+) 2 10
12
Теперь возьмём и запакуем наше число “10” в контекст. Используем контекст из типа данных Option/Maybe:
type 'a option = None | Some of 'a
> (+) 2 (Some 10)
Теперь мы не можем просто так применить функцию (+)2 к запакованному значению. Для этого наш контейнер должен предоставлять какие-то функции, с помощью которых мы можем работать с запакованными значениями. Причем результат применения одной и той же функции к одному и тому же значению может быть разным - это зависит от контекста. Это основная идея, на которой базируются функторы, аппликативные функторы, монады.

<aside> 💡 Функтор - тип данных, который можно рассматривать как контейнер или обертку для других типов данных, и который имеет функцию fmap (сигнатура ниже), позволяющий применять функцию к значениям внутри функтора, не изменяя его структуры.
</aside>
module type Functor = sig
type 'a t (* t - как раз наша "обертка/контейнер" *)
val fmap : ('a -> 'b) -> 'a t -> 'b t
val (<$>): ('a -> 'b) -> 'a t -> 'b t (* тот же самый fmap, просто син.сахар *)
end
Рассмотрим монаду Option - она также является функтором (спойлер - любая монада является функтором).
module OptionFunctor : Functor = struct
type 'a t = 'a option
let fmap f = function
| None -> None
| Some x -> Some (f x)
end
Допустим у нас есть запакованное в контекст значение “4”: Some 4.
Мы хотим прибавить к значению в контексте число “2”, но просто сделать так мы не сможем:
> Some 4 + 2
Error: This expression has type 'a option
but an expression was expected of type int
Для этого как раз нам и нужна функция fmap, которое принимает функцию (которая принимает обычное значение и возвращает обычное значение) и запакованное в контекст значение и возвращает запакованное в контекст новое значение:
> fmap (+2) (Some 4)
Some 6
Однако этого всего недостаточно, чтобы предоставленный выше тип являлся функтором. Для того, чтобы он был функтором, необходимо, чтобы для него выполнялось два закона:
<aside>
💡 Закон идентичности (Identity Law):
Применение функции fmap с тождественной функцией id к функтору не должно изменить его.
</aside>