Как стать автором
Обновить

Комментарии 35

Прекрасно, вы почти уловили суть. Теперь допишите к классу Result правильные методы map и flatMap. И тогда всё будет совсем красиво и в соответсвии с теорией.

Да я вот читаю уже четвертый пост, и все думаю… ну неужели те, кому это интересно, все еще не читали скажем вот это: https://dzone.com/articles/whats-wrong-java-8-part-iv? Ведь все уже расписано в мае 2014 года!

Во второй стате этой серии я упомянул, что существует немало введений в Optional, в том числе и на русском языке, в том числе и на Хабре. Их намного меньше чем книг с кулинарными рецептами или книг по йоге. Но это можно обьяснить более узкой аудиторией и более узким вопросом.
Не умаляя достоинств других tutorial и статей Optional, включая цитируемую Вами статью, рискну утверждать, что в них не «все уже расписано» а иногда, по моему мнению, кое-что не так преподнесено. Поэтому я и взялся за это дело.
Ну а кто хорошо разбираетсч в теме, может попытаться написать ещё одну кулинарную книгу, курс по йоге или… введение в Optional (Шутка).

Так еже ли бы вы их читали… Если бы читали — у вас скорее всего не возникало бы вопросов, почему у вашего класса должны быть методы map и flatMap.


Ну а кто хорошо разбираетсч в теме, может попытаться написать ещё одну кулинарную книгу, курс по йоге или… введение в Optional (Шутка).

Писать очередное введение в монады — это давно уже моветон. Уже лет пять как каждый новый автор извиняется за графоманию )))

Я думаю всё дело в том, что Вам хотелось бы, чтобы читатели видели в Optional монаду. А я пытался добиться, стобы читатели ассоциировали Optional с футляром с дополнительными возможностями. Я думаю, с практической точки зрения моя аналогия практикующим программистам намного полезнее.
Знать что монады существуют, безусловно полезно. Но в практической деятельности нужны (в Java) классы, которые содержат столько полезных методов, что из-за них монаду уже и не видно.
Так стоит ли вообще на монаде заострять внимание и вообще упоминать о ней, если её не видно?

Футляры? Ну вот вам футляры: https://habrahabr.ru/post/183150/. Я же говорю — все уже написано сто раз.


Я думаю, с практической точки зрения моя аналогия практикующим программистам намного полезнее.

Говорите за себя. Я и есть практикующий программист в чистом виде, и мне ваша аналогия кажется а) устаревшей б) неудачной. Потому что во-первых, таких уже было сотни, а во-вторых, они были лучше.

Если предложение начинается с «Я думаю», это и означает «говорить за себя». Аналогия с футляром введена во в орой статье серии. Те кто считают эту аналогию неинтересной, неудачной и т.д., по-видимому не читали третью и четвертую часть. У Вас тоже была такая возможность. В аннотациях к статьям серии я старался не обещать лишнего и относительно точно описать, о чём пойдет речь.
Если Вы судите о качестве сотни материалов, которые все были лучше моего, вы должны были потратить на их простениеи уйму времени. Для меня это не очень сочетается с моим представлением о практикующих программистах.
В чём дело? Тоже зуб болит, как и у меня? (Я действительно только от зубного врача вернулся :)
Может подождать, перетерпеть боль?
Для меня это не очень сочетается с моим представлением о практикующих программистах

Лично я учусь с тех пор, как начал программировать, а это ровно 40 лет. Если вы сегодня, при наличии интернета, не читаете хотя бы по статье в день… ну это ваши проблемы, не мои. Что такого странного в сотнях публикаций за десяток лет?


Одно из умений высоко квалифицированного программиста — это умение манипулировать абстракциями (в основном математическими). В моей практике (а это сотни людей за последние 10 лет) видна четкая корреляция — люди, которые являются самыми высококвалифицированными, в 100% случаев способны распознать за частным случаем Option/MayBe что-то общее, например монаду, и сделать из этого выводы. Для таких людей знать несколько разных парадигм (ООП, функциональное программирование, что-то специальное типа Map Reduce) — это норма.


Те же, кто не способен видеть за одним классом абстракцию, как правило люди низко квалифицированные, даже если называются не junior.


Вы пишете для таких? Не, ну на здоровье конечно, но...


Даже в таком случае я не понимаю, нахрена кому-то читать четыре части поста, которые описывают один класс, в котором всего 349 строк? На анализ кода такого размера у меня лично уходит порядка 5 минут. "Туториал" в четырех частях, про 19 методов, каждый из которых не длиннее 10 строк (потому что львиная доля этих 349 строк это javadoc? Серьезно?

Я пишу эту серию для тех, кто пока по каким-либо причинам не использует вообще либо использует не полностью возможности класса Optional. Независимо от названия их позиции.
Про монады я уже отвечал в другом комментарии. Я не считаю, что указание на то что Optional это (в некотором смысле) монада так уж поможет читателям в практическом использовании класса. А встревать в споры с адептами на тему это действительно монада или нет, я в этой серии не хочу.
Документация Oracle по новым возможностям Java 8 очень лаконична. Читать коды класса полезно, если хорошо представляешь, как его применять.
Это почти эмпирический закон — чем абстрактнее класс в программировании или формула в математике или физике — тем дольше про неё надо говорить.
Optional — не исключение.
И кстати, будет ещё одна статья в этой серии.
Стоит, потому что если вы знаете про что-то, что оно монада, то вы сразу знаете, что там есть fmap, bind, join, return, аппликативное применение. Вы сразу знаете что можете комбинировать стелки Клейсли и при этом получать ожидаемый результат. И это знание даётся вам почти даром, если конечно вы знаете что такое монады.
А вы пропустили самое интересное — как делать композицию вычислений, возвращающих Result.
Эта серия статей — tutorial с конкретными целями, которые не совпали с Вашими ожиданиями. Так бывает. То что интересно для Вас, может быть неинтересно для других и наоборот. Возможно, со временем интересы изменятся.
Автору остаётся только надеяться, что труд не пропал зря.
А не попробовать ли Вам, как это сделал Moriline представить свои интерпертации или расширения рассмотренных в статьях примеров? Например — с композицией? Это был бы конструктивный вклад в Хабр.
Относительно монад мне нравится вот эта цитата: «… монады — это шаблон, а не определенный тип. Монады — это форма, они абстрактные интерфейс (не в смысле Java) больше, чем конкретные данные. В результате любое руководство, основанное на примерах, обречено на неполноты и неудачи. [...] Единственный способ понять монады — увидеть их для них: математическую конструкцию.» Daniel Spiewak. В этом переводе
А моё руководство основано на примерах.
Так что я против упоминания монад в моем tutorial :)
Стоит, потому что если вы знаете про что-то, что оно монада, то вы сразу знаете, что там есть fmap, bind, join, return, аппликативное применение

Если бы в Java был стандартный интерфейс Monad с перечисленными методами и как минимум 2 стандартных класса, которые его реализовывают, то про это стоило бы говорить. Иначе — только запутывать читателя.
С моей точки зрения, уже сейчас семантику map и flatMap в Optional не просто соотнести с семантикой этих же методов в Steeam.
Он и есть. Если Typename — монада, то у него есть методы of, map,flatMap. И, что характерно, они реализованы у Optional и Stream.
Я бы согласился с Вами, если в документации на Optional и Stream в разделе All Superinterfaces стояло в числе других Monad. Но этого нет. Есть методы с одинаковыми названиями. А это далеко не одно и то же.
Одобрение как от школьного усителя. Хотя у «ученика» волос на голове больше седых, чем иных. Но всё равно — спасибо.
Ну а если серьёзно — класс писался для собственного использования. Пока мне ни map ни flatMap не понадобились. Понадобится — напишем. А заодно и filter.
Если хотите, сделайте сами pull requestt в упомянутый проект.
Очень интересная тема для дискуссии, навеянная Вашим замечанием: «Какие методы из арсенала функционального программирования были бы уместны в этом классе?“
В качестве певого набора кандидатов можно рассмотреть методы, реализованные в Optional.
Нимагу, потому как в джава продакшене я как свинья в апельсинах. Вы упорно отбиваетесь от изучения функторов, монад, алгебраических типов данных. Хотя то что вам нужно называется монада ошибки.
Не воспринимаю упрёк на свой счёт. Как я понимаю, по «вы» Ва понимаете сообщество профессиональных Java программистов.
Кстати, я лично программировал и программирую далеко не только на Java.
Я могу понять Вашу досаду, что замечательные языки с замечательными функциональными концепциями в своих недрах никак не отвоюют себе достойную нишу в продакшине. Вы думаете, что это всё из-за лени и костности практикующих программистов?
Я имел в виду Вас лично. Почему этого нет в экосистеме? Думаю потому, что не приспособленный синтаксис и высокий порог входа. Есть SkalaZ, но его синтаксис я вообще не знаю.

Это есть в экосистеме. Возможно не в самом runtime, но есть в десятках (я не преувеличиваю) реализаций от сторонних производителей. Скажем Atlassian fugue, или очень старая Functional Java.

В фуге копроизведение, а хочется монаду. Впрочем без do нотации это будет весьма ограниченно. Без частичного применения некрасиво, могут спасти лямбды, но всё равно некрасиво. Ещё про замыкания забыл, не знаю что с ними в джаве.

Ну, я не обещал, что будет как в хаскеле. Не будет — система типов местами слабовата. Но для практического применения тот Either который имеется, вполне пригоден.

1.
Для этого нам необходимо уметь возвращать не только результат обработки в случае успеха, но и информацию об обнаруженной проблеме в случае неудачи. Для наглядности я сравнивал в предыдущих статьях 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"));
	}
Как раз в Haskell исключения сделаны правильно, через монадический трансформер ErrorT. Возможно есть какие-то ньюансы с IO, но там всегда так.
Я с Вами не согласен. Исключения пришли в С из ассемблера как абстракция аппаратных прерываний «нормального» потока вычислений. С моей точки зрения — это крайность, которую надо использовать как средство обработки брутальных процессорных проблем типа нехватки памяти. Такие исключения случаются редко (если у Вас не барахлит компьютер) и обрабатывать их стоит на верхних этажах.
Я не призываю КАЖДЫЙ вызов оформлять как Result, а только те, где вероятность неудачи относительно высока и для дальнейшей обработки надо знать причину неудачи.

Поддерживаю. У меня при чтении этой части в голове крутилась мысль "а чем исключения не угодили?".

Описанный вариант хорош при использовании стримообразного программирования, при наличии методов map и forEach он органично вписывается в поток. А исключения туда сложнее вписывать (предполагаю, такова задумка авторов Stream API).

В методах обработки можно перехватывать Exceptions и возвращать их в Result как FAILURE.
Именно по этой причине Мартин Одерски придумал Either[Left, Right] :)
Этот паттерн использовался и раньше, как мне кажется, в частности во внутренностях STL. Я назвал класс Result из прагматических соображений.

Я, в свое время, набросал вот такое (буду благодарен за конструктивную критику, т.к. решено чуть иначе):


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

Result
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 можно наследовать другие подобные классы.
Улучшения также возможны. Класс Union лучше сделать abstract, чтобы его нельзя было вызывать напрямую. А функции isFirstPresent, isSecondPresent лучше сделать protected и заменять их предметными аналогами типа isSuccess и isException.

А абстрактный дженерик класс это не перебор? Как-то мне это глаз резануло и в реализации автора статьи. Вроде бы это два разных механизма обеспечения абстракции и интуиция шепчет мне «не смешивай». Но, я не профи, может и врет интуиция.
PS: прямо как с алкоголем

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории