Comments 35
Да я вот читаю уже четвертый пост, и все думаю… ну неужели те, кому это интересно, все еще не читали скажем вот это: https://dzone.com/articles/whats-wrong-java-8-part-iv? Ведь все уже расписано в мае 2014 года!
Не умаляя достоинств других tutorial и статей Optional, включая цитируемую Вами статью, рискну утверждать, что в них не «все уже расписано» а иногда, по моему мнению, кое-что не так преподнесено. Поэтому я и взялся за это дело.
Ну а кто хорошо разбираетсч в теме, может попытаться написать ещё одну кулинарную книгу, курс по йоге или… введение в Optional (Шутка).
Так еже ли бы вы их читали… Если бы читали — у вас скорее всего не возникало бы вопросов, почему у вашего класса должны быть методы map и flatMap.
Ну а кто хорошо разбираетсч в теме, может попытаться написать ещё одну кулинарную книгу, курс по йоге или… введение в Optional (Шутка).
Писать очередное введение в монады — это давно уже моветон. Уже лет пять как каждый новый автор извиняется за графоманию )))
Знать что монады существуют, безусловно полезно. Но в практической деятельности нужны (в Java) классы, которые содержат столько полезных методов, что из-за них монаду уже и не видно.
Так стоит ли вообще на монаде заострять внимание и вообще упоминать о ней, если её не видно?
Футляры? Ну вот вам футляры: https://habrahabr.ru/post/183150/. Я же говорю — все уже написано сто раз.
Я думаю, с практической точки зрения моя аналогия практикующим программистам намного полезнее.
Говорите за себя. Я и есть практикующий программист в чистом виде, и мне ваша аналогия кажется а) устаревшей б) неудачной. Потому что во-первых, таких уже было сотни, а во-вторых, они были лучше.
Если Вы судите о качестве сотни материалов, которые все были лучше моего, вы должны были потратить на их простениеи уйму времени. Для меня это не очень сочетается с моим представлением о практикующих программистах.
В чём дело? Тоже зуб болит, как и у меня? (Я действительно только от зубного врача вернулся :)
Может подождать, перетерпеть боль?
Для меня это не очень сочетается с моим представлением о практикующих программистах
Лично я учусь с тех пор, как начал программировать, а это ровно 40 лет. Если вы сегодня, при наличии интернета, не читаете хотя бы по статье в день… ну это ваши проблемы, не мои. Что такого странного в сотнях публикаций за десяток лет?
Одно из умений высоко квалифицированного программиста — это умение манипулировать абстракциями (в основном математическими). В моей практике (а это сотни людей за последние 10 лет) видна четкая корреляция — люди, которые являются самыми высококвалифицированными, в 100% случаев способны распознать за частным случаем Option/MayBe что-то общее, например монаду, и сделать из этого выводы. Для таких людей знать несколько разных парадигм (ООП, функциональное программирование, что-то специальное типа Map Reduce) — это норма.
Те же, кто не способен видеть за одним классом абстракцию, как правило люди низко квалифицированные, даже если называются не junior.
Вы пишете для таких? Не, ну на здоровье конечно, но...
Даже в таком случае я не понимаю, нахрена кому-то читать четыре части поста, которые описывают один класс, в котором всего 349 строк? На анализ кода такого размера у меня лично уходит порядка 5 минут. "Туториал" в четырех частях, про 19 методов, каждый из которых не длиннее 10 строк (потому что львиная доля этих 349 строк это javadoc? Серьезно?
Про монады я уже отвечал в другом комментарии. Я не считаю, что указание на то что Optional это (в некотором смысле) монада так уж поможет читателям в практическом использовании класса. А встревать в споры с адептами на тему это действительно монада или нет, я в этой серии не хочу.
Документация Oracle по новым возможностям Java 8 очень лаконична. Читать коды класса полезно, если хорошо представляешь, как его применять.
Это почти эмпирический закон — чем абстрактнее класс в программировании или формула в математике или физике — тем дольше про неё надо говорить.
Optional — не исключение.
И кстати, будет ещё одна статья в этой серии.
А вы пропустили самое интересное — как делать композицию вычислений, возвращающих Result.
Автору остаётся только надеяться, что труд не пропал зря.
А моё руководство основано на примерах.
Так что я против упоминания монад в моем tutorial :)
Стоит, потому что если вы знаете про что-то, что оно монада, то вы сразу знаете, что там есть fmap, bind, join, return, аппликативное применение
Если бы в Java был стандартный интерфейс Monad с перечисленными методами и как минимум 2 стандартных класса, которые его реализовывают, то про это стоило бы говорить. Иначе — только запутывать читателя.
С моей точки зрения, уже сейчас семантику map и flatMap в Optional не просто соотнести с семантикой этих же методов в Steeam.
Ну а если серьёзно — класс писался для собственного использования. Пока мне ни map ни flatMap не понадобились. Понадобится — напишем. А заодно и filter.
Если хотите, сделайте сами pull requestt в упомянутый проект.
Очень интересная тема для дискуссии, навеянная Вашим замечанием: «Какие методы из арсенала функционального программирования были бы уместны в этом классе?“
В качестве певого набора кандидатов можно рассмотреть методы, реализованные в Optional.
Кстати, я лично программировал и программирую далеко не только на Java.
Я могу понять Вашу досаду, что замечательные языки с замечательными функциональными концепциями в своих недрах никак не отвоюют себе достойную нишу в продакшине. Вы думаете, что это всё из-за лени и костности практикующих программистов?
Это есть в экосистеме. Возможно не в самом runtime, но есть в десятках (я не преувеличиваю) реализаций от сторонних производителей. Скажем Atlassian fugue, или очень старая Functional Java.
Для этого нам необходимо уметь возвращать не только результат обработки в случае успеха, но и информацию об обнаруженной проблеме в случае неудачи. Для наглядности я сравнивал в предыдущих статьях Optional футляром. Развивая эту аналогию, в данном случае нам необходим футляр с двойным дном.Для этого и придумали исключения. Почему Вы снова возвращаетесь к методам возвращения кода ошибки( или текста ошибки) вместо использования исключений? Ведь использование исключений во время потока исполнения программы намного лучше. Что и доказывает их внедрение в поздних версиях языков( PHP, Haskell и т.д.).
2. Если реализовать Ваш новый тип и КАЖДЫЙ метод его будет использовать, то какова будет реализация обработки таких ошибок на более высоком уровне? Как будет выглядеть код? Например в таком выдуманном случае:
public void name(IRequest req) {
Request request = req.getOriginalParam();
Collection<Group> groups = new Groups().findByName(new ConnectionStorage("x", 10L).getConnection(), new Hash("z", 100).getHash(), request.getParam("name"));
List<User> result = new FilterByAge(new Users().findByGroup(groups, new Hash("y", 10).getHash(), new ConnectionStorage("w", 20).getConnection()), request.getParam("age"));
}
Я не призываю КАЖДЫЙ вызов оформлять как Result, а только те, где вероятность неудачи относительно высока и для дальнейшей обработки надо знать причину неудачи.
Поддерживаю. У меня при чтении этой части в голове крутилась мысль "а чем исключения не угодили?".
Описанный вариант хорош при использовании стримообразного программирования, при наличии методов map
и forEach
он органично вписывается в поток. А исключения туда сложнее вписывать (предполагаю, такова задумка авторов Stream API).
Я, в свое время, набросал вот такое (буду благодарен за конструктивную критику, т.к. решено чуть иначе):
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.stream.Stream;
/**
* Объект-контейнер, который может содержать один из двух типов объектов.
*/
class Union<T1, T2> {
private final Optional<T1> first;
private final Optional<T2> second;
Union(T1 first, T2 second) {
assert (first == null && second != null) || (first != null && second == null);
this.first = Optional.ofNullable(first);
this.second = Optional.ofNullable(second);
}
public boolean isFirstPresent() {
return first.isPresent();
}
public boolean isSecondPresent() {
return second.isPresent();
}
public <U> Stream<U> map(BiFunction<T1, T2, U> function) {
return Stream.of(function.apply(first.orElse(null), second.orElse(null)));
}
public void forEach(Consumer<? super T1> whenValue1, Consumer<? super T2> whenValue2) {
first.ifPresent(whenValue1);
second.ifPresent(whenValue2);
}
}
final class SuccessOrFailure extends Union<String, BatchExceptions> {
private SuccessOrFailure(final String batchName, final BatchExceptions someException) {
super(batchName, someException);
}
public static SuccessOrFailure createFromSuccess(final String batchName) {
return new SuccessOrFailure(batchName, null);
}
public static SuccessOrFailure createFromFailure(final BatchExceptions someExceptions) {
return new SuccessOrFailure(null, someExceptions);
}
}
Улучшения также возможны. Класс Union лучше сделать abstract, чтобы его нельзя было вызывать напрямую. А функции isFirstPresent, isSecondPresent лучше сделать protected и заменять их предметными аналогами типа isSuccess и isException.
Объект в футляре или Optional в Java 8 и Java 9. Часть 4: «Как футляром с двойным дном закрыть дыру в Java“