Монады с Java 8
В интересах помочь понять, что такое монада, может ли кто-нибудь представить пример с помощью java? Возможны ли они?
Лямбда-выражения возможны с использованием java, если вы загружаете ldbd-совместимый JDK8 с предварительным выпуском здесь http://jdk8.java.net/lambda/
Пример lambda с использованием этого JDK показан ниже, может ли кто-нибудь предоставить сравнительно простую монаду?
public interface TransformService {
int[] transform(List<Integer> inputs);
}
public static void main(String ars[]) {
TransformService transformService = (inputs) -> {
int[] ints = new int[inputs.size()];
int i = 0;
for (Integer element : inputs) {
ints[i] = element;
}
return ints;
};
List<Integer> inputs = new ArrayList<Integer>(5) {{
add(10);
add(10);
}};
int[] results = transformService.transform(inputs);
}
Ответы
Ответ 1
Просто FYI:
Предлагаемый JDK8 Необязательный класс удовлетворяет трем демонстрирует, что.
Все, что требуется, это Monad, чтобы обеспечить две функции, которые соответствуют законам три.
Две функции:
Пожалуйста, см. выше gist для демонстрации java трех законов.
ПРИМЕЧАНИЕ. Одной из ключевых вещей, которые следует понимать, является подпись функции для применения в монадическом контексте: она берет тип необработанного значения и возвращает монадический тип.
Другими словами, если у вас есть экземпляр Optional<Integer>
, функции, которые вы можете передать его методу flatMap
, будут иметь подпись (Integer) -> Optional<U>
, где U
- тип значения, который не должен быть Integer
, например String
:
Optional<Integer> maybeInteger = Optional.of(1);
// Function that takes Integer and returns Optional<Integer>
Optional<Integer> maybePlusOne = maybeInteger.flatMap(n -> Optional.of(n + 1));
// Function that takes Integer and returns Optional<String>
Optional<String> maybeString = maybePlusOne.flatMap(n -> Optional.of(n.toString));
Вам не нужен какой-либо интерфейс Monad для кодирования этого способа или думать так. В Scala вы не кодируете интерфейс Monad (если вы не используете библиотеку Scalaz...). Похоже, что JDK8 предоставит Java-пользователям возможность использовать этот стиль прикованных монадических вычислений.
Надеюсь, что это будет полезно!
Обновление. здесь.
Ответ 2
Java 8 будет иметь lambdas; монады - совершенно другая история. Их достаточно сложно объяснить в функциональном программировании (о чем свидетельствует большое количество учебников по этому вопросу в Haskell и Scala).
Монады - типичная особенность статически типизированных функциональных языков. Чтобы описать их в OO-talk, вы могли бы представить интерфейс Monad
. Классы, которые реализуют Monad
, затем будут называться "монадическими", при условии, что при реализации Monad
реализация подчиняется тому, что известно как "законы монады". Затем язык предоставляет некоторый синтаксический сахар, который делает работу с экземплярами класса Monad
интересным.
Теперь Iterable
в Java не имеет ничего общего с монадами, но в качестве примера того типа, который специально рассматривает компилятор Java (синтаксис foreach
, который поставляется с Java 5), рассмотрите следующее:
Iterable<Something> things = getThings(..);
for (Something s: things) { /* do something with s */ }
Итак, хотя мы могли бы использовать методы Iterable
Iterator
(hasNext
и company) в старом стиле for
, Java предоставляет нам этот синтаксический сахар в качестве особого случая.
Так же, как классы, реализующие Iterable
и Iterator
, должны подчиняться законам Iterator
(Пример: hasNext
должен возвращать false
, если нет следующего элемента), который будет полезен в синтаксисе foreach
существовало бы несколько монадических классов, которые были бы полезны с соответствующей записью do
(как она называется в Haskell) или обозначением Scala for
.
Итак -
- Каковы хорошие примеры монадических классов?
- Каким будет синтаксический сахар для работы с ними?
В Java 8 я не знаю - мне известно о лямбда-нотации, но я не знаю другого специального синтаксического сахара, поэтому я должен привести вам пример на другом языке.
Монады часто служат в качестве классов контейнеров (примеры являются примерами). Java уже имеет java.util.List
, который явно не является монадическим, но здесь Scala s:
val nums = List(1, 2, 3, 4)
val strs = List("hello", "hola")
val result = for { // Iterate both lists, return a resulting list that contains
// pairs of (Int, String) s.t the string size is same as the num.
n <- nums
s <- strs if n == s.length
} yield (n, s)
// result will be List((4, "hola"))
// A list of exactly one element, the pair (4, "hola")
Какой (грубо) синтаксический сахар для:
val nums = List(1, 2, 3, 4)
val strs = List("hello", "hola")
val results =
nums.flatMap( n =>
strs.filter(s => s.size == n). // same as the 'if'
map(s => (n, s)) // Same as the 'yield'
)
// flatMap takes a lambda as an argument, as do filter and map
//
Здесь показана функция Scala, где используются монад для обеспечения понимания списков.
Итак, a List
в Scala является монадой, потому что она подчиняется законам монады Scala, которые предусматривают, что все реализации монады должны иметь соответствующие методы flatMap
, map
и filter
(если вы заинтересованные в законах, в блоге "Monads are Elephants" есть лучшее описание, которое я нашел до сих пор). И, как вы можете видеть, лямбды (и HoF) абсолютно необходимы, но не достаточны для практического использования такого рода вещей.
Там есть куча полезных монад, кроме контейнеровозов. У них есть все виды приложений. Моей любимой должна быть монада Option
в Scala (монада Maybe
в Haskell), которая представляет собой тип обертки, который приводит к нулевой безопасности: страница Scala API для Option
монады имеет очень простой пример использования: http://www.scala-lang.org/api/current/scala/Option.html
В Haskell монады полезны для представления IO, как способ обойти тот факт, что немонодический код Haskell имеет неопределенный порядок выполнения.
Наличие лямбда - первый маленький шаг в мир функционального программирования; монады
требуют как соглашения монады, так и достаточно большой набор используемых монадических типов, а также синтаксический сахар, чтобы сделать работу с ними забавой и полезной.
Так как Scala, возможно, является языком, наиболее близким к Java, который также позволяет (монадическое) функциональное программирование, посмотрите на этот учебник по Monad для Scala, если вы заинтересованы (по-прежнему):
http://james-iry.blogspot.jp/2007/09/monads-are-elephants-part-1.html
Беглый googling показывает, что есть хотя бы одна попытка сделать это в Java: https://github.com/RichardWarburton/Monads-in-Java -
К сожалению, объяснение монад в Java (даже с lambdas) так же сложно объяснить полномасштабное объектно-ориентированное программирование в ANSI C (вместо С++ или Java).
Ответ 3
Несмотря на то, что монады могут быть реализованы на Java, любые вычисления, связанные с ними, обречены стать грязной комбинацией дженериков и фигурных скобок.
Я бы сказал, что Java определенно не язык для использования, чтобы проиллюстрировать их работу или изучить их смысл и сущность. Для этого гораздо лучше использовать JavaScript или заплатить дополнительную цену и узнать Haskell.
В любом случае, я предупреждаю вас, что я только что реализовал монадную монадию , используя новую Java 8 lambdas. Это определенно проект для домашних животных, но он работает в нетривиальном тестовом случае.
Вы можете найти его в мой блог, но я расскажу вам несколько деталей.
Государственная монада - это в основном функция от состояния к паре (состояние, содержимое). Обычно вы предоставляете государству общий тип S и контент общего типа A.
Поскольку у Java нет пар, мы должны моделировать их с помощью определенного класса, пусть назовем его Scp (state-content pair), который в этом случае будет иметь общий тип Scp<S,A>
и конструктор new Scp<S,A>(S state,A content)
. После этого мы можем сказать, что монадическая функция будет иметь тип
java.util.function.Function<S,Scp<S,A>>
который является @FunctionalInterface
. Это означает, что его один и единственный метод реализации может быть вызван без его именования, передавая лямбда-выражение с правильным типом.
Класс StateMonad<S,A>
является главным образом оболочкой вокруг функции. Его конструктор может быть вызван, например. с
new StateMonad<Integer, String>(n -> new Scp<Integer, String>(n + 1, "value"));
Монада штата хранит функцию как переменную экземпляра. Затем необходимо предоставить публичный метод доступа к нему и передать его в состояние. Я решил назвать его s2scp
( "state to state-content pair" ).
Чтобы завершить определение монады, вы должны предоставить единицу (aka return) и метод bind (aka flatMap). Лично я предпочитаю указывать единицу как статическую, а bind - член экземпляра.
В случае государственной монады единица должна быть следующей:
public static <S, A> StateMonad<S, A> unit(A a) {
return new StateMonad<S, A>((S s) -> new Scp<S, A>(s, a));
}
while bind (как член экземпляра):
public <B> StateMonad<S, B> bind(final Function<A, StateMonad<S, B>> famb) {
return new StateMonad<S, B>((S s) -> {
Scp<S, A> currentPair = this.s2scp(s);
return famb(currentPair.content).s2scp(currentPair.state);
});
}
Вы заметили, что bind должен ввести общий тип B, потому что это механизм, который позволяет связывать гетерогенные монады состояний и дает этой и любой другой монаде замечательную возможность переместить вычисления из типа в тип.
Я бы остановился здесь на Java-коде. Комплексный материал находится в проекте GitHub. По сравнению с предыдущими версиями Java, lambdas удаляет множество фигурных скобок, но синтаксис все еще довольно запутан.
Так же, как и в стороне, я показываю, как аналогичный код монады штата может быть написан на других основных языках. В случае Scala bind (который в этом случае должен быть называться flatMap) читается как
def flatMap[A, B](famb: A => State[S, B]) = new State[S, B]((s: S) => {
val (ss: S, aa: A) = this.s2scp(s)
famb(aa).s2scp(ss)
})
тогда как привязка в JavaScript - моя любимая; 100% функциональный, скудный и средний, но, конечно, безличный:
var bind = function(famb){
return state(function(s) {
var a = this(s);
return famb(a.value)(a.state);
});
};
< бесстыдная > Я режу несколько углов здесь, но если вас интересуют детали, вы найдете их в моем блоге WP. </shameless>
Ответ 4
Единственный способ понять монады - это написать несколько библиотек комбинаторов, заметить полученное дублирование, а затем обнаружить для себя, что монады позволяют исключить это дублирование. Открывая это, каждый строит некоторую интуицию для того, что такое монада... но эта интуиция - не та вещь, которую вы можете общаться с кем-то другим напрямую - кажется, что каждый должен пройти тот же опыт обобщения на монады из некоторых конкретных примеров библиотеки комбинаторов. тем не мение
Здесь я нашел некоторые материалы, чтобы узнать Mondas.
надеюсь быть полезным для вас тоже.
codecommit
Джеймс-iry.blogspot
debasishg.blogspot
Ответ 5
Здесь о монадах, которые трудно понять: монады - это шаблон, а не определенный тип. Монады - это форма, они абстрактные интерфейс (не в смысле Java) больше, чем конкретные данные состав. В результате любое руководство, основанное на примерах, обречено на неполноты и неудачи. [...] Единственный способ понять монады - увидеть их для них: математическую конструкцию.
Монады не метафоры Daniel Spiewak
Монады в Java SE 8
Список монад
interface Person {
List<Person> parents();
default List<Person> greatGrandParents1() {
List<Person> list = new ArrayList<>();
for (Person p : parents()) {
for (Person gp : p.parents()) {
for (Person ggp : p.parents()) {
list.add(ggp);
}
}
}
return list;
}
// <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)
default List<Person> greatGrandParents2() {
return Stream.of(parents())
.flatMap(p -> Stream.of(p.parents()))
.flatMap(gp -> Stream.of(gp.parents()))
.collect(toList());
}
}
Возможно, монада
interface Person {
String firstName();
String middleName();
String lastName();
default String fullName1() {
String fName = firstName();
if (fName != null) {
String mName = middleName();
if (mName != null) {
String lName = lastName();
if (lName != null) {
return fName + " " + mName + " " + lName;
}
}
}
return null;
}
// <U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper)
default Optional<String> fullName2() {
return Optional.ofNullable(firstName())
.flatMap(fName -> Optional.ofNullable(middleName())
.flatMap(mName -> Optional.ofNullable(lastName())
.flatMap(lName -> Optional.of(fName + " " + mName + " " + lName))));
}
}
Monad - это общий шаблон для инкапсуляции вложенного потока управления.
То есть способ создания повторно используемых компонентов из вложенных императивных идиом.
Важно понимать, что монада - это не просто общий класс-оболочка с плоской картой.
Например, ArrayList
с методом flatMap
не будет монадой.
Поскольку законы монады запрещают побочные эффекты.
Монаш - формализм. Он описывает структуру, независимо от содержания или значения.
Люди борются со связанными с бессмысленными (абстрактными) вещами.
Поэтому они придумывают метафоры, которые не являются монадами.
См. также:
беседа между Эриком Мейджером и Гиладом Брахой.
Ответ 6
Это сообщение в блоге дает пошаговый пример того, как вы можете реализовать тип (интерфейс) Monad на Java, а затем использовать его для определения монады Maybe, как практического приложения.
Этот пост объясняет, что в язык Java встроена одна монада, подчеркивая, что монады более распространены, чем многие программисты могут подумать и что кодеры часто непреднамеренно изобретают их.
Ответ 7
Мне нравится думать о монадах в slighlty более математической (но все же неформальной) моде. После этого я объясню отношение к одной из мозаик Java 8 CompletableFuture.
Прежде всего, монада M
является функтором. То есть, он преобразует тип в другой тип: если X
- это тип (например, String
), мы имеем другой тип M<X>
(например, List<String>
). Более того, если мы имеем типы преобразований/функций X -> Y
, мы должны получить функцию M<X> -> M<Y>
.
Но для такой монады больше данных. У нас есть так называемая единица, которая является функцией X -> M<X>
для каждого типа X
. Другими словами, каждый объект X
может быть естественным образом завернут в монаду.
Наиболее характерными данными монады, однако, является ее произведение: функция M<M<X>> -> M<X>
для каждого типа X
.
Все эти данные должны удовлетворять некоторым аксиомам, таким как функториальность, ассоциативность, единичные законы, но я не буду вдаваться в подробности здесь, и это также не имеет значения для практического использования.
Теперь мы можем вывести еще одну операцию для монад, которая часто используется как эквивалентное определение для монадов, операция привязки: значение/объект в M<X>
может быть связано с функцией X -> M<Y>
, чтобы получить другое значение в M<Y>
. Как мы это достигаем? Ну, сначала применим функториальность к функции для получения функции M<X> -> M<M<Y>>
. Затем мы применяем монадическое произведение к цели для получения функции M<X> -> M<Y>
. Теперь мы можем подключить значение M<X>
, чтобы получить значение в M<Y>
по желанию. Эта операция связывания используется для объединения нескольких монодических операций.
Теперь перейдем к примеру CompletableFuture, т.е. CompletableFuture = M
. Подумайте об объекте CompletableFuture<MyData>
как о некотором вычислении, которое выполняется асинхронно и которое в результате приведет к некоторому объекту MyData
в будущем. Какие здесь монадические операции?
-
Функторность
- реализуется с помощью метода
thenApply
: сначала выполняется вычисление, и как только результат будет доступен, функция, которая предоставляется thenApply
, применяется для преобразования результата в другой тип
- монадический блок реализуется с помощью метода
completedFuture
: как сообщает документация, итоговое вычисление уже завершено и сразу дает заданное значение.
- монадическое произведение не реализуется функцией, но операция привязки ниже эквивалентна ему (вместе с функториальностью), а ее семантическое значение имеет следующий вид: при вычислении типа
CompletableFuture<CompletableFuture<MyData>>
, что вычисление асинхронно дает другое вычисление в CompletableFuture<MyData>
, что в свою очередь дает некоторое значение в MyData
позже, так что выполнение обоих вычислений после другого дает одно вычисление в целом
- результирующая операция связывания реализуется методом
thenCompose
Как вы видите, вычисления теперь могут быть завернуты в особый контекст, а именно асинхронность. Общие монадические структуры позволяют нам связывать такие вычисления в данном контексте. CompletableFuture
, например, используется в структуре Lagom, чтобы легко создавать высокоасинхронные обработчики запросов, которые прозрачно резервируются с помощью эффективных пулов потоков (вместо обработки каждого запроса выделенным потоком).
Ответ 8
Несмотря на все разногласия по поводу того, удовлетворяет ли Optional
законам Монады или нет, я обычно люблю смотреть на Stream
, Optional
и CompletableFuture
одинаково. По правде говоря, все они предоставляют flatMap()
и это все, что меня волнует, и позвольте мне принять "вкусную композицию побочных эффектов" (цитируемую Эриком Мейером). Таким образом, мы можем иметь соответствующие Stream
, Optional
и CompletableFuture
следующим образом:
![57518402-d4e59f00-7310-11e9-8ae0-f41a91aafd64.png]()
Что касается монад, я обычно flatMap()
думая только о flatMap()
(из курса "Принципы реактивного программирования" Эрика Мейера):
![Eric-Meijer-flatMap]()