Монады с 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, чтобы обеспечить две функции, которые соответствуют законам три.

Две функции:

  • Поместите значение в монадический контекст

    • Haskell Возможно: return/Just
    • Scala Опция: Some
    • Функциональный Java-вариант: Option.some
    • JDK8 Дополнительно: Optional.of
  • Применить функцию в монадическом контексте

    • Haskell Возможно: >>= (aka bind)
    • Scala Опция: 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));

Вам не нужен какой-либо интерфейс 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