Монады с Java 8

java java-8 monads optional

31600 просмотра

8 ответа

33815 Репутация автора

В интересах помочь понять, что такое монада, может ли кто-нибудь привести пример с использованием 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);
    }
Автор: NimChimpsky Источник Размещён: 19.11.2012 12:58

Ответы (8)


5 плюса

1462 Репутация автора

Единственный способ понять монады - это написать несколько библиотек комбинаторов, заметить полученное дублирование, а затем обнаружить для себя, что монады позволяют исключить это дублирование. Открывая это, каждый строит некоторую интуицию для того, что такое монада ... но эта интуиция - не та вещь, которую вы можете общаться с кем-то другим напрямую - кажется, что каждый должен пройти тот же опыт обобщения на монады из некоторого конкретного примеры библиотек комбинаторов. тем не мение

Здесь я нашел некоторые материалы, чтобы узнать Mondas.

надеюсь быть полезным для вас тоже.

codecommit

Джеймс-iry.blogspot

debasishg.blogspot

Автор: Morteza Adi Размещён: 19.11.2012 01:20

55 плюса

13917 Репутация автора

Java 8 будет иметь лямбды; Монады это совсем другая история. Их достаточно сложно объяснить в функциональном программировании (о чем свидетельствует большое количество учебных пособий по этому предмету в Haskell и Scala).

Монады являются типичной особенностью статически типизированных функциональных языков. Чтобы описать их в ОО-говорить, вы можете представить Monadинтерфейс. Классы, которые реализуют Monad, затем будут называться «монадическими», при условии, что при реализации Monadреализация подчиняется так называемым «законам монад». Затем язык предоставляет некоторый синтаксический сахар, который делает работу с экземплярами Monadкласса интересной.

Теперь Iterableв Java нет ничего общего с монадами, но в качестве примера типа, который компилятор Java обрабатывает специально ( foreachсинтаксис, который поставляется с Java 5), ​​рассмотрим это:

Iterable<Something> things = getThings(..);
for (Something s: things) {  /* do something with s */ }

Таким образом , в то время как мы могли бы использовать Iterable«s Iteratorметодов ( hasNextи компании) в старом стиле forцикла, Java предоставляет нам этот синтаксический сахар как частный случай .

Так же, как классы, которые реализуют Iterableи Iteratorдолжны подчиняться Iteratorзаконам (Пример: hasNextдолжен возвращать, falseесли нет следующего элемента), чтобы быть полезными в foreach синтаксисе - существовало бы несколько монадических классов, которые были бы полезны с соответствующей doнотацией (как это вызывается в Haskell ) или forобозначение Скалы .

Так -

  1. Каковы хорошие примеры монадических классов?
  2. Как будет выглядеть синтаксический сахар для работы с ними?

В Java 8 я не знаю - мне известны лямбда-нотации, но я не знаю другого специального синтаксического сахара, поэтому мне придется привести вам пример на другом языке.

Монады часто служат контейнерными классами (например, списки). У Java уже есть java.util.Listчто, очевидно, не монадическое, но вот Scala:

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методы (если вы заинтересованы в законах, то «Монада Слоны» запись в блоге имеет лучшее описание I» нашел пока). И, как вы можете видеть, лямбды (и HoF) абсолютно необходимы, но не достаточны для практического использования такого рода вещей.

Есть еще куча полезных монад, кроме контейнерных. У них есть все виды приложений. Моей любимой должна быть Optionмонада в Scala ( Maybeмонада в Haskell), которая является типом обертки, который обеспечивает нулевую безопасность : страница Scala API для Optionмонады имеет очень простой пример использования: http: //www.scala-lang. org / api / current / scala / Option.html В Haskell монады полезны для представления ввода-вывода как способ обойти тот факт, что немонадный код на Haskell имеет неопределенный порядок выполнения.

Лямбда - это первый маленький шаг в мир функционального программирования; монады требуют как соглашения о монаде, так и достаточно большого набора используемых монадических типов, а также синтаксического сахара, чтобы работа с ними была интересной и полезной.

Поскольку Scala, возможно, является языком, наиболее близким к Java, который также позволяет (монадическое) функциональное программирование, посмотрите этот учебник Monad для Scala, если вы (все еще) заинтересованы: http://james-iry.blogspot.jp/2007/09/ монады-ARE-слоны-часть-1.html

Беглый поиск в Google показывает, что в Java есть хотя бы одна попытка сделать это: https://github.com/RichardWarburton/Monads-in-Java -

К сожалению, объяснить монады в Java (даже с лямбдами) так же сложно, как объяснить полномасштабное объектно-ориентированное программирование в ANSI C (вместо C ++ или Java).

Автор: Faiz Размещён: 19.11.2012 02:15

2 плюса

743 Репутация автора

В этом посте приводится пошаговый пример того, как можно реализовать тип (интерфейс) монады в Java, а затем использовать его для определения монады Maybe, как практического приложения.

В этом посте объясняется, что в язык Java встроена одна монада, подчеркивая тот факт, что монады встречаются чаще, чем могут подумать многие программисты, и что кодеры часто непреднамеренно их изобретают .

Автор: Katie J. Ots Размещён: 18.12.2012 04:48

75 плюса

2193 Репутация автора

Решение

Просто к вашему сведению:

Предлагаемый необязательный класс JDK8 удовлетворяет трем законам монады . Вот суть, демонстрирующая это.

Все, что нужно для того, чтобы быть Монадой, это предоставить две функции, которые соответствуют трем законам.

Две функции:

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

    • Haskell's Maybe: return/Just
    • Вариант Скала: Some
    • Опция функциональной Java: Option.some
    • JDK8 необязательно: Optional.of
  2. Применить функцию в монадическом контексте

    • Haskell's Maybe: >>=(ака bind)
    • Вариант Скала: flatMap
    • Опция функциональной Java: flatMap
    • JDK8 необязательно: flatMap

Пожалуйста, см. Выше суть для демонстрации трех законов в 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 использовать этот стиль цепных монадических вычислений .

Надеюсь, это полезно!

Обновление: Blogged об этом здесь .

Автор: ms-tg Размещён: 12.11.2013 02:55

10 плюса

1401 Репутация автора

Несмотря на то, что монады могут быть реализованы в Java, любое вычисление, в котором они участвуют, обречено стать грязной смесью обобщений и фигурных скобок.

Я бы сказал, что Java определенно не тот язык, который нужно использовать, чтобы проиллюстрировать их работу или изучить их значение и сущность. Для этой цели гораздо лучше использовать JavaScript или заплатить некоторую дополнительную цену и изучить Haskell.

В любом случае, я сообщаю вам, что я только что реализовал монаду состояния, используя новые лямбды Java 8 . Это определенно любимый проект, но он работает на нетривиальном тестовом примере.

Вы можете найти его в моем блоге , но я дам вам некоторые подробности здесь.

Монада состояний - это, в основном, функция от состояния к паре (состояние, содержимое) . Вы обычно присваиваете состояние универсальный тип S, а контенту - универсальный тип A.

Поскольку в Java нет пар, мы должны смоделировать их с помощью определенного класса, назовем его Scp (пара состояния-содержимого), который в этом случае будет иметь универсальный тип 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 (также называемый return ) и метод bind (также известный как flatMap ). Лично я предпочитаю указывать unit как static, тогда как bind является членом экземпляра.

В случае государственной монады единица должна быть следующей:

public static <S, A> StateMonad<S, A> unit(A a) {
    return new StateMonad<S, A>((S s) -> new Scp<S, A>(s, a));
}

в то время как связывание (как член экземпляра):

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);
    });
}

Вы заметите, что связывание должно вводить универсальный тип 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.

Автор: Marco Faustinelli Размещён: 27.04.2014 07:33

4 плюса

1611 Репутация автора

Вот что такое монады, что трудно понять: монады - это образец, а не определенный тип. Монады - это форма, они представляют собой абстрактный интерфейс (не в смысле 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методом не будет монады. Потому что законы монады запрещают побочные эффекты.

Монада это формализм . Он описывает структуру независимо от содержания или значения. Люди борются с относящимися к бессмысленным (абстрактным) вещам. Таким образом, они придумали метафоры, которые не являются монадами.

Смотрите также: разговор между Эриком Мейером и Гиладом Брача.

Автор: user2418306 Размещён: 08.01.2016 01:28

0 плюса

302 Репутация автора

Мне нравится думать о монадах чуть более математически (но все же неформально). После этого я объясню связь с одной из монад 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 для простого создания высоко асинхронных обработчиков запросов, которые прозрачно резервируются эффективными пулами потоков (вместо обработки каждого запроса выделенным потоком).

Автор: Werner Thumann Размещён: 07.07.2017 06:41

0 плюса

3866 Репутация автора

Несмотря на все споры о Optionalудовлетворяющихах, или нет, законы Монады, я обычно люблю смотреть на Stream, Optionalи CompletableFutureтаким же образом. По правде говоря, все они обеспечивают, flatMap()и это все, что меня волнует, и позвольте мне принять « со вкусом состав побочных эффектов » (цитируется Эрик Мейер). Таким образом, мы можем иметь соответствующий Stream, Optionalи CompletableFutureследующим образом:

Что касается монад, я обычно упрощаю их, только размышляя flatMap()(из курса « Принципы реактивного программирования » Эрика Мейера):

Эрик-Мейер-flatMap

Автор: Miguel Gamboa Размещён: 10.05.2019 09:55
Вопросы из категории :
32x32