Комментарии 12
Разве?
Но это всё равно что сказать, что автомобиль является (расширяет) ДВС. Несомненно, в типичном автомобиле есть двигатель и ровно один. И все методы двигателя могут быть делегированы автомобилем, его несущим. Но ведь автомобиль от этого не становится двигателем.
Насколько я понимаю, в данном случае функтор, который конструируется из монады — единственный. Морфизм f: A=>B
конвертируется в M[A] => M[B]
путём использования двух операций, предоставляемых монадой. Вначале функцию f
превращаем в вычисление, применяя нулевое вычисление η к результату функции: φ = λa.ηfa
, а затем конструируем результирующую функцию в lifted-категории с использованием моноидного умножения μ: λma.μma φ
.
В Scala это выглядит, как привычный способ выразить map
через flatMap
и pure
:
def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B]
def pure[A](x: A): F[A]
def map[A, B](fa: F[A])(f: A => B): F[B] = flatMap(fa)(a => pure(f(a)))
Эндофункторов, над которыми строится моноид, действительно много, но ни один из них, насколько я могу судить, не имеет такой формы, как этот — (A=>B) => (M[A] => M[B])
.
Я рассматриваю монаду с точки зрения программирования. В статье Monads for functional programming (Philip Wadler) монада определяется как тройка, состоящая из конструктора типа, отображения, превращающего обычное значение в этот тип ("нулевое" вычисление), и отображения, связывающего два вычисления в одно.
То, что монада также является функтором, следует из того, что монада (как и функтор) позволяет преобразовать простые функции над базовыми типами в lifted-функции над сконструированным типом, то есть позволяет выполнить отображение категории обычных функции в категорию lifted-функций.
Несмотря на это, монада — более универсальная конструкция, чем функтор. Монада не только отображает обычные функции в lifted, но и предоставляет дополнительные возможности работы с функциями типа A => M[B]
, которые включают конструируемый тип — "вычислениями". При этом, сам конструируемый тип M[B]
оказывается включен в класс объектов категории (поэтому, по-видимому, категория эндофункторов; при этом конструктор типа M[?]
не включен в объекты категории). И вместо двух изолированных категорий, связанных функтором, мы оперируем одной категорией, в структуре которой выделены функции особого вида — вычисления, — и объекты — M[B]
, инкапсулирующие (потенциальный) результат вычисления. Для этих функций/вычислений монада предоставляет базовый набор действий, соответствующий моноиду, — нулевое действие (без вычислений), и комбинирование двух вычислений.
Так как данные не могут произвольно меняться, то нет причины их скрывать, и вместо сокрытия данных теперь используются открытые типы, где данные — публичны. (Тем самым, среди трёх столпов ООП — полиморфизма, наследования и инкапсуляции, — один оказывается несколько задвинут в сторону.)
Вы путаете инкапсуляцию и сокрытие. Модификатор private на данных — это слишком мелко для такого важного столпа как инкапсуляция.
Да, вы правы. Сокрытие и инкапсуляция хоть и идут рука об руку во многих языках, всё-таки концептуально отличаются. Неизменяемые типы данных сами по себе не исключают инкапсуляцию, а лишь подрывают рациональные основания для сокрытия данных.
Инкапсуляция, как мне кажется, подразумевает, что как только мы объявили методы доступа к данным, следующий логичный шаг — скрыть прямой доступ к этим данным, чтобы исключить нарушение абстракции. Без сокрытия от инкапсуляции остаётся лишь возможность сложить в один объект данные и функции, что, по-видимому, имеет ограниченную ценность. А вот инкапсуляция вместе с сокрытием — хороший инструмент абстрагирования.
Классы типов в Scala (с небольшим обзором библиотеки cats)