Монады с Java 8
в интересах помочь понять, что такое монада, может ли кто-нибудь привести пример с использованием java ? Возможно ли это ?
лямбда-выражения возможны с помощью java, если вы загрузите предрелизную лямбда-совместимую JDK8 отсюда http://jdk8.java.net/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);
}
7 ответов:
просто FYI:
предложил JDK8 дополнительно класс соответствует трех законы монады. Вот это суть демонстрации что.
все, что нужно быть монадой, чтобы обеспечить два функции, которая соответствует три законы.
две функции:
место значение в монаде контекст
- может быть, Хаскелл:
в Scala:return
/Just
Some
- функциональная опция Java:
Option.some
- JDK8 опционально:
Optional.of
применить функция в монадическом контексте
- может быть, Хаскелл:
в Scala:>>=
(он жеbind
)flatMap
- функциональная опция Java:
flatMap
- JDK8 опционально:
flatMap
смотрите выше 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));
вам не нужен какой-либо интерфейс монады, чтобы код таким образом, или думать таким образом. В Scala вы не кодируете интерфейс Monad (если только вы не используете библиотеку Scalaz...). Похоже, что JDK8 позволит людям Java использовать этот стиль цепные монадические вычисления как хорошо.
надеюсь, что это это полезно!
обновление: сообщила об этом здесь.
в Java 8 будет иметь лямбды; монады-это совсем другая история. Их достаточно сложно объяснить в функциональном программировании (о чем свидетельствует большое количество учебных пособий по этому предмету в Haskell и Scala).
монады являются типичной особенностью статически типизированных функциональных языков. Чтобы описать их в OO-speak, вы могли бы представить себе
Monad
интерфейс. Классы, которые реализуютMonad
тогда будет называться "монадическим", при условии, что в реализацииMonad
реализация подчиняется так называемым "законам монады". Затем язык предоставляет некоторые синтаксический сахар что делает работу с экземплярамиMonad
интересный класс.теперь
Iterable
в Java не имеет ничего общего с монадами, но в качестве примера типа, который компилятор Java обрабатывает специально (foreach
синтаксис, который пришел с Java 5), рассмотрим это:Iterable<Something> things = getThings(..); for (Something s: things) { /* do something with s */ }
так что пока мы могли бы использовать
Iterable
' sIterator
методы (hasNext
и компания) в старый стильfor
цикл, Java предоставляет нам этот синтаксический сахар как особый случай.так же, как классы, реализующие
Iterable
иIterator
должны подчинятьсяIterator
законы (пример:hasNext
должен возвратитьfalse
если нет следующего элемента), чтобы быть полезным вforeach
синтаксис-там было бы несколько монадическом классы, которые были бы полезны с соответствующимdo
нотация (как она называется в Haskell) или Scalafor
нотация.так
- каковы хорошие примеры монадических классов?
- как бы выглядел синтаксический сахар для борьбы с ними?
в Java 8 я не знаю - я знаю о лямбда-нотации, но я не знаю о другом специальном синтаксическом сахаре, поэтому мне придется дать вам пример на другом языке.
монады часто служат контейнер классы (списки являются примером). Java уже имеет
java.util.List
который, очевидно, не монадический, но вот скала: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, где монады используются для обеспечения списочные включения.
так
List
в Scala есть монада, потому что она подчиняется законам монады Scala, которые предусматривают, что все реализации монады должны иметь соответствиеflatMap
,map
иfilter
методы (Если вы заинтересованы в законах запись в блоге "монады-слоны" имеет лучшее описание, которое я нашел до сих пор). И, как вы можете видеть, лямбды (и Хоф) абсолютно необходимые а не достаточное чтобы сделать такую вещь полезной на практике.есть куча полезных монад, кроме контейнерных. Они имеют все виды применений. Мой любимый должен быть
Option
монада в скале (theMaybe
монады в Haskell), который является тип обертки, который приводит к нулевое безопасность: страница API Scala для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
беглый гугл показывает, что есть по крайней мере одна попытка сделать это в Java: https://github.com/RichardWarburton/Monads-in-Java -
к сожалению, объяснить монады в Java (даже с лямбдами) так же сложно, как объяснить полномасштабное объектно-ориентированное программирование в ANSI C (вместо C++ или Java).
несмотря на то, что монады могут быть реализованы на 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
("состояние к состоянию-содержание пары").для завершения определения монады вы должны предоставить unit (он же возвращение) и связать (он же flatMap) метод. Лично я предпочитаю указывать unit как статический, тогда как bind является членом экземпляра.
в случае государственной монады, единица должна быть следующей:
public static <S, A> StateMonad<S, A> unit(A a) { return new StateMonad<S, A>((S s) -> new Scp<S, A>(s, a)); }
в то время как 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, лямбды удаляют много фигурных скобок, но синтаксис все еще довольно запутан.
просто в стороне, я показываю, как подобный код монады состояния может быть написан на других основных языках. В случае 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.
вот вещь о монадах, которую трудно понять: монады-это узор, а не конкретный тип. Монады-это форма, они абстрактны интерфейс (не в смысле Java) больше, чем они являются конкретными данными структура. В результате, любой пример-приводимый в учебнике, обречена на неполнота и недостаточность. [...] Единственный способ понять монады-увидеть их такими, какие они есть: математическая конструкция.
монады не метафоры Даниил Спивак
монады в 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)))); } }
Монада - generic шаблон вложенные инкапсуляция потока управления. Т. е. способ создания многоразовых компонентов из вложенных императивных идиом.
важно понимать, что монада-это не просто универсальный класс-оболочка с квартиры карта операции. Например,
ArrayList
СflatMap
метод не будет монадой. Потому что законы монады запретить побочных эффектов.Монада -формализм. Он описывает структуру, независимо от содержания или смысла. Люди борются с отношением к бессмысленным (абстрактным) вещам. Поэтому они придумывают метафоры, которые не являются монадами.
Читайте также: разговор между Эриком Мейером и Гилад Брача.
этот блог дает пошаговый пример того, как вы можете реализовать тип монады (интерфейс) в Java, а затем использовать его для определения монады Maybe в качестве практического приложения.
этот пост объясняет, что есть одна монада, встроенная в язык Java, подчеркивая тот факт, что монады более распространены, чем многие программисты могут подумать, и что кодеры часто ненароком изобретать их.
мне нравится думать о монадах в легкой более математической (но все же неформальной) манере. После этого я объясню отношение к одной из монад Java 8 CompletableFuture.
прежде всего, монада
M
Это функтор. То есть он преобразует тип в другой тип: IfX
тип (например,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
: как говорится в документации, результирующее вычисление уже завершено и сразу дает заданное значение- монадический продукт не реализуется a функция, но приведенная ниже операция привязки эквивалентна ей (вместе с функториальностью) и ее семантическое значение просто следующее: Учитывая вычисление типа
CompletableFuture<CompletableFuture<MyData>>
это вычисление асинхронно дает другое вычисление вCompletableFuture<MyData>
что в свою очередь дает некоторое значение вMyData
позже, поэтому выполнение обоих вычислений после другого дает одно вычисление в общей сложности- результирующая операция связывания реализуется методом
thenCompose
как вы видите, вычисления теперь могут быть обернуты в особый контекст, а именно asynchronicity. Общие монадические структуры позволяют нам цеплять такие вычисления в данном контексте.
CompletableFuture
, например, используется в лагом фреймворк позволяет легко создавать высоко асинхронные обработчики запросов, которые прозрачно поддерживаются эффективными пулами потоков (вместо обработки каждого запроса выделенным потоком).