Pull to refresh
0
@user_manread⁠-⁠only

User

Send message
Я всё же повторю ещё раз — попробуйте отказаться от привычных шаблонов, вы в них реально погрязи.

>> код должен работать с любой вызывающей стороной без изменений самого кода.

Он и работает. Никаких изменений не требуется.

>> Скажет вызывающая сторона работать асинхронно — код будет работать асинхронно, скажет работать параллельно — будет работать параллельно, скажет работать последовательно, с блокировками — будет работать последовательно, с блокировками.

А вот слово «скажет» — это передача информации. О передаче дополнительных параметров в ТЗ слов нет, так что опять вы в плену шаблона.

>> Оно, конечно, может быть многопоточным. Никто вас не ограничивал. Главное — оно не должно быть параллельным. Каждый из query выполняется только после того, как предыдущий query вернул результат.

Для последовательного исполнения задач нужно просто написать последовательность их вызовов. Монады при этом не нужны.

>> На await ничего не синхронизируется. Я не понимаю, я же вам указал, что именно происходит при await, как он рассахаривается. Зачем вы чушь свою повторяете? Забудьте про await если для вас это чересчур сложная конструкции, вот код, эквивалентный коду с await: f(callback) { x(() => y((z) => callback(z))); }
Что здесь с чем синхронизируется?

В одном потоке — и то нужно синхронизировать. Вопрос только в реализации потоков. Но вы опять рассматриваете лишь свой шаблон, и потому не понимаете простых вещей.

>> Ваш код будет выполнен неправильно. Потому что запросы должны идти последовательно, а не параллельно.

Пишите ТЗ понятно и будет вам счастье. Ну а про последовательное исполнение — см. выше.

>> Моя функция с yield работает без знания контекста. Благодаря монадам.
>> Напоминаю, вы сказали, что монады не нужны — вот и покажите мне аналогичный код без монад.

И моя функция работает без знания контекста. И без монад. И даже в двух реализациях. Ещё про третью выше говорил — без синхронизации, всё в одном потоке последовательно.

Я не знаю, как нужно не хотеть отвечать, что бы вот так как вы постоянно отвечать одно и то же, но при это как-то не лениться набирать все эти слова. Совершенно непонятна ваша мотивация. Не хотите отвечать? Так не отвечайте. Не хотите объяснять, но хотите ответить? Тогда вопрос — а зачем такой цирк?

В общем, если вы всё же осилите выход за пределы шаблона, то вы наверняка всё поймёте, но тут, похоже, ещё и психология как-то влияет. Вы что, просто оправдываетесь, видя убедительные аргументы? Не были бы они убедительными, вы ведь просто могли бы промолчать, мол вот дурак сам себя выпорол, но вы же отвечаете. Не понимаю я вас.
Вы предлагаете отправить 2 запроса параллельно и утверждаете, что подобная логика будет работать быстрее и правильнее.

Интересно узнать, где вы нашли подобные заявления с моей стороны?

Посмотрите на функцию из ТЗ и мою реализацию внимательнее. Там есть зависимость от результата первого вызова. И там нет предложенного вами подхода.
Ваш шаблон называется — хочу как в браузере, и только так, как я привык.

Объясняю:

Вы не дали никакой информации по вызывающей ваш код стороне. Даже более — навели туману про HTTP и ещё чего-то там. Значит это может быть что угодно. Но тогда с чего вы взяли, что «что угодно» — это ваш любимый браузер?

Далее из вашего следования шаблону работы с браузерным кодом вы делаете вывод, что выполнение задачи может быть исключительно однопоточным. Это не так.

Вот цитата из ECMA-262:
8.4.1 EnqueueJob
8. Perform any implementation or host environment defined processing of pending.

Затем вы опять шаблонно (для фронтэнда) описываете используемые понятия, такие как блокировка, синхронность, асинхронность и т.д.:

>>Так там нету блокировки, и параллельного выполнения нету. Там асинхронный код.

Объясняю:

Блокировка, это когда задача останавливается и чего-то ждёт (задача заблокирована). Отсутствие блокировки — это когда задача продолжает выполняться после неблокирующего вызова.

Синхронный код обязан синхронно с каким-то другим кодом начать исполнение с заданной точки. Асинхронный код не имеет таких ограничений и может выполняться с любой точки, независимо от любого другого кода.

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

Это означает, что ваш код синхронизируется на точке await. Ваш код не может выполняться без учёта этой точки синхронизации. Если он будет выполняться без учёта синхронизации, то вы получите неготовый результат и искажённый смысл работы алгоритма (например, в виде исключения).

Далее вы опять настаиваете на своём привычном шаблоне:

>> Вы оба раза написали код, который работает не так, как исходный. И сделали это потому, что не знаете как работает async/await.

Я действительно не являюсь абсолютным экспертом по JS, но тем не менее, последовательное выполнение вашего кода я понять могу. Даже больше скажу — я почитал дополнительно по модели исполнения JS и, в общем-то, вынес для себя кое-что полезное (чего не знал до общения с вами), но всё равно ваш код от этого не становится каким-то особенным, он по прежнему последовательно и синхронно, блокируя исполнение до получения результата, выполняет функции из вашей вызывающей yob-ы.

Теперь более высокий уровень:

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

На самом деле ваши намёки, скорее всего, говорят о так называемой инверсии контроля, а не об асинхронности. То есть абсолютно синхронно и последовательно некая внешняя задача вызывает вашу yob-у, ну а yoba как-то по разному отвечает, в зависимости от контекста.

Что нужно для реализации вашего ТЗ в вранианте «инверсия контроля»? Как минимум — нужно знать контекст. То есть когда и в зависимости от чего менять поведение yob-ы. Вы такой информации не привели. Поэтому, опять же, мне остаётся лишь один вариант — менять поведение через переданные мне вызываемые функции, что я и сделал. Не попал в вами любимый шаблон? Ну что-ж, не я же такое ТЗ составил.

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

Ладно, это всё опять же была лишь лирика, потому что основная тема — монады.

И, видимо, по монадам вы изначально хотели донести, что оказывается, монады могут получать функцию, и когда-то (в зависимости от контекста) её запускать. Ага, это всё та же инверсия контроля. То есть вы отдаётесь своим монадам и они вас дёргают по своему усмотрению. Этим вы, видимо, хотели показать «простоту» передачи callback-ов, но вы не поняли очень простую вещь — в однопоточном режиме, используемом в браузерах, все эти передачи функций приводят к эквиваленту гораздо более простого кода — последовательного и без монад.

Вот пример:
f(()=>g(()=>h()));

В однопоточной среде эквивалентно:
f();
g();
h();

Что проще? Месиво из лямбд или просто последовательное перечисление того, что нужно сделать?

И да, этот пример никак не относится к асинхронности. Здесь строго синхронно вызывается одна и та же последовательность действий, но в первом варианте (вашем) — это всё запутано, а потому кому-то может показаться, что там есть асинхронность. Во втором же варианте всё ясно. Вот и вам бы научиться излагать свои мысли примерно на таком же уровне понятности.

ЗЫ.

Я подчеркну — с асинхронностью, в отличии от вас, я привык работать не в браузерах, а в реальных приложениях, требующих высокой производительности, а потому ваш однопоточный подход для меня ничем неинтересен (и да, я его не изучал до уровня эксперта). Но тем не менее, я хоть пытаюсь понять, что вы там себе придумали (какие шаблоны ждёте), ну а вы же даже и не собирались ничего понимать, просто повторяете одно и то же — дай мне как я привык. И как с таким подходом можно работать?
Если бы это было так, то при любом await код на жсе бы вставал колом, т.к. жс — однопоточный и блокировка потока это блокировка всего жса

А как же Worker-ы?

В общем так — в скрипте вы выдали строго последовательный код, блокировку на IO и параллельное выполнение чего-то другого вы тоже не продемонстрировали. Я же первый раз повторил ваше творчество, ну а второй — уже сделал как надо.

По ТЗ — оно, мягко говоря, ни о чём. Зачем нужно было демонстрировать функцию, строго последовательно что-то там выполняющую? У меня на эту тему единственная мысль — вы ожидали, что я буду повторять некий привычный лично вам шаблон. Ну а я не повторил. И вы в ступоре — как так, все же делают по шаблону! Отсюда ваше настойчивое повторение каких-то «хинтов» и прочих намёков. Да, прямо сказать сложно, ведь нужно сначала самому понять — я мыслю шаблонно. Ну что-ж, тогда я вам указываю на вашу проблему.

По изменению второго варианта моего кода. Главный вопрос — зачем? Городить какой-то неэффективный шаблон исключительно потому, что спрашивающий привык видеть именно этот шаблон? Это глупо. Приведённый код эффективен, распараллеливает задачу по максимуму (для большего вы не дали информации в своём ТЗ). Ухудшать код ради удовлетворения лично вас у меня желания нет.

Ну и концептуально. Речь, вообще-то, шла о монадах. Но стандартный трюк с уходом в частности опять на сцене. Не скажу, что это делается намеренно, потому что это просто очередной шаблон. Это же так привычно — увидел неоднозначность «в чужом глазу» и сразу смело бросился «исправлять», забыв о сути обсуждения.

Ну да не вы один такой. Ответы всех остальных ещё хуже — краткий выкрик на очень узкую тему, нередко основанный на эмоциях, а не на попытках подумать. То есть опять сплошной шаблон.

Далее в комментариях, наверняка, перейдём к шаблону «ты перешёл на личности», а потому повторюсь — не я перешёл на личности, а в все живёте по шаблону. Например по шаблону «указать на шаблон перехода на личности».

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

Автор начальной версии ТЗ немого добавил к своим предыдущим соображениям, но из этих соображений видно, что вменяемого ТЗ не будет. Из этого далее последует интересный вывод.

Сначала о первоначальной версии ТЗ. Его автор действительно хотел получить другой результат, но как он сам ранее говорил — он пишет подобный код на монадах даже не задумываясь. Поэтому и получил то, что получил.

Далее придётся пояснять всем отметившимся «знатокам», почему они все неправы. Да, обидно, ЧСВ страдает, но надо. Умные примут этот урок, ну а остальным уже ничем не поможешь.

Итак, массово расставленные в коде предыдущей версии ТЗ ключевые слова await означают, что первоначальный код (внезапно) точно такой же синхронный, как и предоставленный в ответ на ТЗ. Поясню коротко, как работает конструкция await («знатоки», не ухмыляйтесь, а прочитайте внимательно). Сначала создаётся обещание. В момент его создания в параллельном потоке запускается выполнение его задачи. А в момент встречи ключевого слова await текущий поток (то есть не тот, в котором выполняет свою задачу обещание) останавливается и ждёт завершения работы обещания. Так вот, густо расставленные await-ы как раз и останавливают текущий поток именно в тех местах, в которых он бы остановился, если бы все эти queryXY запускались строго последовательно. И это превращает выполнение запросов в синхронный процесс. Зачем это надо? Понятия не имею. Автор предложил мне показать код вместо дальнейших пояснений. Поэтому я и не стал думать о таких вещах, а тупо сделал копию предложенного. Да, у меня вызов тоже синхронный, да, непонятно, зачем это нужно, но здесь важно одно — такая реализация соответствует ТЗ. А если ТЗ «не очень», то какие претензии к реализации? Trash in, trash out.

Далее автор попытался поправиться и выдал код на yield-ах. Но я не стану далее загонять его в угол (да и с самого начала не хотел, ведь мог же автор поинтересоваться, например, как запустить задачу в параллельном потоке). Скажу только, что на yield-ах всё получилось не лучше — теперь получением результат управляет вызывающая сторона, но как раз она-то и не знает, когда закончится выполнение запросов, да и вообще про запросы ничего не знает. Она будет дёргать код, который без всяких параллельных потоков будет заставлять вызывающую сторону ждать в самом привычном однопоточном режиме. То есть как всё было синхронным, так и осталось. Хотя последовательность выходных значений у функции изменилась. Но это, видимо, не то, что ожидал автор.

В общем, не повторяя акты из марлезонского балета, сразу приведу код, который (как требуют «знатоки») является реально асинхронным. Вот он:

public class Service<T2,T1 extends Iterable<T2>,T3> extends Synchronizer
{
	public T3 yoba(Behavior<T1,T2,T3> b) throws InterruptedException, 

ExecutionException
	{
		T1 x1 = b.query1();
		reset();
		if (b.f(x1))
		{
			for (T2 x2:x1)
			{
				if (b.g(x2))
				{
					async(()->b.query31(x2));
					async(()->b.query32(x2));
				}
				else async(()->b.query33(x2));
				async(()->b.query34(x2));
			}
			async(()->b.query41(x1));
		}
		else async(()->b.query42(x1));
		Future<T3> f=async(()->b.query5(x1));
		complete();
		return f.get();
	}
}

И вот слегка модифицированный под потребность Synchronizer:

public class Synchronizer
{
	private ExecutorService executorService;
	private List<Future<?>> futures = new ArrayList<Future<?>>();

	public void complete() throws InterruptedException, ExecutionException
	{
		for (Future<?> f:futures)
			f.get();
		futures.clear();
	}
	
	public void reset()
	{
		for (Future<?> f:futures)
			f.cancel(true);
		futures.clear();
	}
	
	public Future<?> async(Runnable r) throws InterruptedException, ExecutionException
	{ return add(executorService.submit(r)); }
	
	public <T> Future<T> async(Callable<T> c) throws InterruptedException, 

ExecutionException
	{ return add(executorService.submit(c)); }
	
	private <T> Future<T> add(Future<T> f)
	{
		futures.add(f);
		return f;
	}
}

Собственно, здесь наглядно видно, что код из ТЗ (теперь уже из двух версий) практически в неизменном виде присутствует как в последнем, так и в первом решении. Код крайне простой.

Код последовательный и понятный. Никаких монад. Никаких лишних приседаний (лифты да разнообразные переопределённые стрелки в смеси с адским деревом математических типов).

Но кроме собственно очевидной из кода простоты, имеем ещё и очевидную простоту получившейся системы в целом. Почему? Потому что все «знатоки» в голос заявляли мне, что мой код синхронный (правда после указания на Java API), а вот автору ТЗ никто не сказал, что и в его коде «что-то не так». Почему не сказал? Да потому что все эти промисы реально сложнее предложенного решения. Ну и оказалось, что никто из «знатоков» не разбирается в том, как работают промисы. А вот в моём коде — разобрались моментально. А всё почему? Да потому, что «делают на монадах не задумываясь» ((с) не моё). А когда видят вменяемое описание происходящего (наследие создателей Java), то привычка делать не задумываясь их не подводит. Потому что наследие стоящее.

Монады (как и JavaScript) прячут от разработчика суть происходящего. Пояснения к функционированию как монад, так и скриптовых конструкций — убогие. Хоть и развелась куча учебников по скрипту, а всё равно подробную цепочку всего происходящего они не дают. Ну а про ФП и говорить нечего — только разбираться с исходным кодом компилятора, иначе — никак. Точнее — иначе опять будет «делают на монадах не задумываясь».

Так в чём же польза от монад? Повторюсь — это всего лишь костыли, делающие программирование на ФЯП более «элегантным» (с некоторых точек зрения). И всё. Более в них смысла нет, одна морока (как и с любыми костылями).

Ну а фанатам ФП остаётся сказать одно — не зазнавайтесь. Ибо молоды вы ещё приводить аргументы в противостотянии ФП — императив. Может лет вам и много, но знаний (и особенно — опыта) у вас очень мало. Императивом занимались реальные монстры, с которыми вы ни в какое сравнение не идёте. Культура разработки на императиве даже не снилась тем университетским лаборантам, которые создали, например, хаскель (и чисто для справки — они не зазнавались). Ну а последовавшие за ними любители и вовсе отметились лишь работами уровня «курсовая» да «дипломная», так откуда у них возьмётся культура разработки? Учитесь у отцов-основателей, забейте своё ЧСВ подальше и изучайте, например, Java, просто потому, что те, кто её создавал, как раз были причастны к той мощной культуре разработки, о которой вы, к сожалению, понятия не имеете. И вот эти отцы никогда бы не стали заявлять что-то вроде «делаю на монадах не задумываясь», потому что, например, синхронизация — это непростая задача, а решать непростые задачи «не задумываясь» — ну вы только что видели, к чему это приводит.

В общем пока всё, умные поймут, дураки разольют нечистоты, ну да к этим «выбросам» мне не привыкать :)
>> Так что в любом случае не засчитывается

Нет, так дело не пойдёт. Навели тумана и теперь троллите. Нехорошо.

Предлагаю следующий план дискуссии:
1) Вы честно признаёте, что показанная реализация соответствует вашему «ТЗ».
2) Вы соглашаетесь с тем, что не указали полной информации в своём ТЗ, а потому там осталось место для различных вариантов реализации.
3) Вы, наконец, указываете, каким же на самом деле должно быть ТЗ, благо моя реализация вам явно показывает, где и что вы недоговорили.
4) Ну и было бы неплохо привести вашу версию кода. Это устранит неоднозначность, свойственную любому ТЗ, а так же не даст вам отвертеться в случае, когда я приведу аналогичный по функционалу и более простой код без монад.

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

ЗЫ.

Надеюсь нет нужды напоминать ваше собственное ТЗ, но для других оставлю ссылку. Текст скопировал, на всякий случай.
Что-то уровень вопросов меня смущает.

Я действительно сделал неоптимально, с вашей подачи проверил — можно оптимальнее. Но вот смысл происходящего в коде объяснять — не ожидал, что это вообще потребуется. А вы, видимо, именно не понимаете смысл происходящего (судя по вопросу).

Выше комментарий точно так же вытекает из непонимания. Правда уровни непонимания, похоже, разные.

Вот эти уровни:
1) Непонимание сути обсуждения.
2) Непонимание сути «ТЗ».
3) Непонимание базового API Java.

С первыми двумя, я надеялся, со стороны сторонников ФП не будет сложностей, но я оказался неправ…

Если же сложность с пунктом №3, то вот здесь всё написано.

Ну и по оптимальности.

Действительно, я «по быстрому» выбрал те интерфейсы, которые даны в пакете function, а вот Callable находится в concurrent. Но разумеется, можно было напрямую использовать Callable и тогда в Synchronizer-е не нужны были бы лямбды.

Какой урок можно вынести из моего ляпа? Очень простой — сторонники ФП свято верят в магические свойства их любимого подхода, и в частности — в компилятор. Мол если программа скомпилировалась, то там просто не может быть проблем! Да, именно так они и заявляют. Не все, но наиболее агрессивная часть в прямом смысле верует в подобную чушь, а потому без всяких сомнений выкладывает её на всеобщее обозрение. И вот на моём отрицательном примере мы видим, что компилирующаяся программа на самом деле неоптимальна, просто потому, что я невнимательно просмотрел всю цепочку исполнения в целом. А сторонники ФП же ведь ожидают, что если всё скомпилировалось, то и мега-оптимизатор за них решит все возможные проблемы производительности, а магическая структура ФП никак не даст им написать явную лажу, ведь там сплошная математика и прочее бла-бла-бла.

В общем — ещё один пункт в список недостатков ФП — этот подход, благодаря связи с математикой, вселяет ложную уверенность в своей правоте. Только никакая математика в мире никогда не исправит человеческую ошибку в таких местах, для которых математика не предназначена. И этот момент сторонники ФП (особенно агрессивная их часть) всегда забывают.

Ну а если сообщество ФП кроме эмоционального «фи» более не имеет возражений, то придётся записать в наш журнал полную и безоговорочную победу здравого смысла над фанатизмом ФП.
Привожу код:
public class Service<T2,T1 extends Iterable<T2>,T3> extends Synchronizer
{
	public T3 yoba(Behavior<T1,T2,T3> b) throws InterruptedException, ExecutionException
	{
		T1 x1 = get(()->b.query1());
		if (test(()->b.f(x1)))
		{
			for (T2 x2:x1)
			{
				if (test(()->b.g(x2)))
				{
					act(()->b.query31(x2));
					act(()->b.query32(x2));
				}
				else act(()->b.query33(x2));
				act(()->b.query34(x2));
			}
			act(()->b.query41(x1));
		}
		else act(()->b.query42(x1));
		return get(()->b.query5(x1));
	}
}

Если непонятен входной параметр, то вот он:
public interface Behavior<T1,T2,T3>
{
	public T1 query1();
	public boolean f(T1 t1);
	public boolean g(T2 t2);
	public void query31(T2 x2);
	public void query32(T2 x2);
	public void query33(T2 x2);
	public void query34(T2 x2);
	public void query41(T1 x1);
	public void query42(T1 x1);
	public T3 query5(T1 x1);
}

Ну и примитивнейшая библиотека, реализацию которой осилит даже ещё не дописавший до конца Hello World начинающий разработчик:
public class Synchronizer
{
	private ExecutorService executorService;
	
	protected <T> T get(Supplier<T> bs) throws InterruptedException, ExecutionException
	{ return executorService.submit(()->bs.get()).get(); }
	
	protected boolean test(BooleanSupplier bs) throws InterruptedException, ExecutionException
	{ return executorService.submit(()->bs.getAsBoolean()).get(); }

	protected void act(Runnable r) throws InterruptedException, ExecutionException
	{ executorService.submit(r).get(); }
}


Собственно выше дана полная реализация вашего «ТЗ». Реализуя приведённый Behavior вы в нём можете хоть с марсом связываться, общая логика сервиса от этого не изменится.

Теперь очевидные преимущества:

1) Нет монад. То есть нет всего того ненужного множества смыслов, которыми грузят мир сторонники ФП. Все эти смыслы с точки зрения реализации ТЗ — просто мусор. Может в каких-то других очень редких случаях эти смыслы полезны, но в вами же предложенном задании — от них толку = абсолютный ноль.

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

3) Кроме того вы обязаны использовать так называемую «do notation», иначе получите месиво из вложенных лямбда-функций, передаваемых таким же образом вложенным монадам. Привлечение do notation в данном случае самым наглядным образом доказывает, что даже сторонники ФП согласны с очевидным фактом — императивно выражать мысли об алгоритмах намного проще (что мы и имеем в предложенном выше коде).

4) Как и было указано ранее, неделимость монады приводит к невозможности использовать раздельно синхронизацию и передачу контейнеров с функционалом, что уменьшает гибкость варианта с ФП.

5) В целом для получения аналогичного по функционалу самостоятельно созданного и работоспособного решения с императивным подходом требуется самая минимальная подготовка, которую обучаемый получает за несколько месяцев. В случае же ФП средний обучаемый убьёт годы, пока поймёт суть происходящего.

Если сказать короче — в угоду следованию функциональной модели ФП приводит к совершенно ненужной сложности, из которой далее следуют выше показанные пункты (и наверняка не только они). Ну а ваше заявление про некую минимальность решения на основе ФП явно не выдерживает критики.
Интересный подход. Однозначность соответствия дискретных и непрерывных функций поможет (или может помочь) доказывать свойства целых чисел, например — простоту, а возможно и делимость (факторизация). Реккурентная последовательность Люка является обобщением для многих последовательностей, в том числе и для последовательности Фибоначчи, но интересная последовательность Люка тем, что позволяет создавать тесты простоты, как вероятностные, так и детерминированные. Для этой последовательности есть аналитическое (нереккурентное) решение, но что даст подход с применением методов, отработанных для непрерывных функций? Может получиться другое аналитическое выражение, что в свою очередь может упростить, например, определение простоты. И это будет строгое решение, если эквивалентность в статье тоже строго доказана (прочитал пока лишь «краткую логику статьи» под спойлером).

В целом дискретные задачи можно было бы решать с привлечением методов для непрерывных функций. А применимость таких методов в дискретных случаях как раз и должна быть доказана автором. Это, на мой скромный взгляд, могло бы быть «научной новизной», но я не настолько глубоко знаком с теорией чисел, что бы гарантировать, что схожие подходы там не используются (анализ функций там присутствует точно). В рамках же базовых курсов теории чисел этого нет.
>> запишите плиз как у вас там будет на listener'ах

Да так же и будет. Но без монад. Точнее — без необходимости привлекать понятие «монада».

Смысл происходящего в вашем примере простой — программа ждёт, пока будет результат. Нужны ли для этого монады? Очевидно — нет. Достаточно просто синхронизации ожидания с доставкой результата.

Но далее вы «поправились», то есть добавили условие неизменности показанной функции при изменяемом функционале программы. И что же для этого нужно? Ну опять же — нужна просто возможность передавать альтернативный функционал внутрь функции. Да, такая передача напоминает шаблон, используемый монадами (или используемый сторонниками ФП). Но повторюсь — передача функции придумана и реализована очень давно, точно ранее появления ФП. Поэтому притягивать за уши монады к именно такому шаблону было бы явно нечестно.

В итоге имеем ту же джава-скриптовую функцию, что и у вас, но с передачей в неё ваших восьми запросов. В ООП логично было бы упаковать восемь запросов в некий контейнер (реализующий интерфейс), для краткости и понятности происходящего. Ну а внутри контейнера никто не мешает реализовывать 8 вызовов как угодно, хоть на 8 классов побить, хоть на 100, хоть в одном всё оставить.

Какие относительно нестандартные концепции мы используем в такой функции? Всего их две — синхронизированное ожидание и передача контейнера с функционалом. Заметим, что концептов более чем один. А вот монада — она всегда одна. То есть если даже согласиться с притягиванием за уши давно известного, но теперь «модно-молодёжно» поданного под соусом монад, то в монадах мы имеем неизбежный недостаток — они неделимы. То есть в них уменьшена гибкость. А гибкость — штука очень полезная. И вот я, со своим старомодным подходом при помощи давно известных принципов/концептов, легко реализую нужную гибкость, разделив ожидание и передачу контейнера на более удобные (в некоторых случаях) части, а в случае ФП с монадами придётся всегда городить «по шаблону», то есть негибко.

Ну и «совсем в итоге» — так зачем же нам монады, если я обошёлся без них, а к тому же они ещё и негибкие? Я вот не хочу, что бы меня нагибали на использование кем-то придуманного шаблона, даже если при этом уверяют, что «всё построено на ужасно умной математике».
Просто к самому паттерну добавляются некоторые встроенные средства для его использования — вам не надо выполнять руками cps-преобразование кода (которое требуется для того, чтобы пробрасывать коллбеки) и огребать от callback hell, за вас все делает либо do-нотация, либо набор монадических комбинаторов, либо (на худой конец) жесткое структурирование через bind.

Вы в этой фразе смешали в кучу много понятий. Но если их разгрести, то мы увидим отсутствие необходимости в монадах. Так же, как нет необходимости в изучении формализации Пеано для вычисления 2+2.

Собственно всё сводится к тому, что ваши «некоторые встроенные средства» прекрасно реализуются на любом языке, и без монад. Хотя, скажем, вложенная типизация (типа T<A<B<C...>>>) пока что мало распространена, но это опять же не монады, а встроенный в компилятор отдельный механизм, отслеживающий проблемы типизации. Это я к ответу участника «PsyHaSTe», там у него типизированные инстансы выдаются за пользу от монад. Может вы что-то другое имели в виду, но ваши жаргонизмы я не понял.
При том данный набор минимален — т.е. если вы попробуете сделать такой «удобный listener», вы в любом случае получите эквивалентную монадам конструкцию.

А вот этот момент, на мой взгляд, очень важен. То есть я пока не вижу «минимальности» набора. Но вы же это однозначно утверждаете. Значит, по всей видимости, у вас есть понимание, почему же этот набор минимальный. Ну и было бы чудесно, если бы вы это понимание изложили. А то получается «у нас всё круто» и никаких доказательств.

Можно хотя бы на примере — что я не смогу сделать на императивном языке без монад? Но лучше с некоторым теоретическим обоснованием. Только не увлекайтесь теорией категорий, я её поверхностно копал (и не только я).

Далее отвечу участнику «PsyHaSTe» (ибо меня тут ограничивают).

>> Никакой копипасты. И какая тут асинхронность?

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

>> программа не дедлочится, и не ддосит шлюз

Ну вот опять никак необоснованное утверждение. По сути вы заявили, что введение магического слова «монады» избавляет нас от нескольких классов ошибок. Но механизм такого избавления, почему-то, вы не раскрываете. А было бы интересно найти отличия от заявлений алхимиков и им подобных. Надеюсь вам есть что сказать.

Ну и вот ваши требования к указанным вами конструкциям:
Тем что дефолты могут быть разные в разных частях приложения. И тем что не во всех языках десериализаторы умеют вызывать конструктор

Исходя из таких пожеланий можно предположить, что монады неким мистическим образом сами угадывают, какие же дефолты у нас будут в разных частях приложения, правильно? И повторюсь — не путайте монады с типизацией, а так же не путайте типизацию с множеством дефолтных значений.
>> Ходить по HTTP в другие сервисы, взаиомдействовать с БД, читать данные из конфига, писать данные в лог — это всё «абстрактные и оторванные от практики вещи»?

Как эти конкретные задачи решаются при помощи монад? Если это опять будет стандартное указание на один из способов работы с асинхронностью, то я выше уже говорил — есть и другие способы. Самый простой пример — просто отдаём listener некой функции и забываем о проблеме. Этот шаблон реализован уже с пол века назад (до появления ФП), и вот теперь его же реализовали «с монадами», то есть наворотили много абстрактных понятий вокруг тривиального обработчика результата запроса. Но зачем эта вся абстракция? Что она меняет? Ведь мы всё тот же listener передаём (да, и в монаду тоже).

Ну и далее логично следует очередная простая проблемка — зачем нам выполнять определения из теорий групп и категорий для listener-а? Что это даёт? Где тут нужны ассоциативность, коммутативность и прочее?

Скажу по другому — математика исследует некое пространство по определённым правилам, это придаёт исследованной части некоторую структурированность и гарантирует выполнение в ней неких закономерностей. Но когда закономерность находится на уровне сложности 2+2, ради одного только такого «достижения» не стоит приплетать математические теории к программированию. Это аналогично приплетению формализации арифметики Пеано ко всем привычным ариметическим действиям в программировании. Представим себе стандартное сложение в любом языке программирования — вместо 2+2 нам пришлось бы работать с чем-то вроде «секвенторов» (должно же звучать «заумно»?), которым пришлось бы передавать «инкрементируемые типы» выполняющие законы ассоциативности, коммутативности и т.д. И да, сторонники такого подхода очень быстро переопределили бы операцию "+", придав ей привычный вид, но конечно же, на входе этой операции были бы всё те же заумные типы, а для понимания операции программистам пришлось бы изучить бездну книг по абстрактной математике и формальному выводу. Да, в итоге все бы привыкли и всё бы вернулось на круги своя, то есть к привычному и короткому 2+2, но что бы дойти до этого простейшего варианта всех бы нагнули на поход через никому ненужные абстрактные дебри. Это и есть тот прекрасный мир, который дают нам монады?

>> То есть «хочу двух наследников одного интерфейса, один из которых работает синхронно, а другой — асинхронно» — это туманная непонятная фигня?

Повторяюсь — это реализуется тривиально. Сначал делается библиотечная функция, которая заботится об асинхронности, а потом этой функции скармливаются синхронные listener-ы. Так при чём же здесь монады?

>> Вопрос — как не писать функцию get_config и не дублировать структуру AppConfig?

Вообще-то в большинстве языков есть возможность задавать дефолтные значения прямо в конструкторе. Чем вас не устраивает такой подход?

ЗЫ.

Если окажется, что для ответа вам понадобится сказать что-то вроде «я имел в виду не только то что написал, но ещё и вот что...», то подчеркну — точность формулировок при рассуждениях о монадах (особенно со стороны их адвокатов) является обязательным атрибутуом, без которого вся математика сразу идёт лесом, потому что это уже не математика. Поэтому попробуйте всё же без упрощающих сокращений и ожидания от собеседника понимания именно того контекста, о котором вы подумали, но никому не сказали.
>> Насколько я понимаю для полноценных монад нужен уровень абстракции выше

Там всё сложнее.

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

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

То есть мы просто и тупо копируем паттерн «монада» из любви к искусству. Точнее — фанаты ФП копируют. А пользы от копирования — только, так сказать, понты в разговорах на хабре.

Я вижу лишь один пример в виде истории хаскеля, а на другие примеры мне просто жалко тратить время из-за, по сути, полной бесполезности такой траты. Пример из хаскеля простой — нужно было как-то «элегантно» запретить менять состояние вне текущей функции, но при этом обеспечить решение таких задач, как ввод-вывод. Сначала там были потоки, которые (особенно с точки зрения математиков) выглядели «неидеоматически». То есть народ нарягался по поводу отсутствия «архитектурной красоты». И вот появилась идея с монадами. Точнее кто-то сочинил краткую запись этой идеи на хаскеле, унаследовав при этом ещё и от теоркатно-групповых понятий, которые на тот момент уже были в хаскеле. И только затем уже к монадам прикрутили паттерн «возьми это и сделай с ним вот это». При этом развели адскую сложность, сочинив по дороге десяток дополнительных паттернов и пару десятков понятий. И всё для чего? Что бы система выглядела «стройной».

Подчеркну — не ради каких-то практических выгод (о которых в статье заявляет автор), а именно ради «стройности». Но со стороны это выглядит (как правильно заметили другие) overarchitected решением. В практической разработке таких архитекторов никто не любит из-за необходимости тратить много личного времени на реализацию их безумств. Хотя начальство, бывает, покупается на «математические основы» и тому подобное.

Глубокие и разветвлённые иерархии наследования в ООП часто приводят именно к плохому пониманию большинством создаваемой программы, а в случае с ФП к иерархиям добавлен целый океан самой абстрактной математики (то есть не имеющей прямых практических приложений, а от того мало кому понятной, ибо никто не способен ответить на простой вопрос — зачем это надо?).

Возможно, при моделировании чего-то столь же абстрактного, как и сама теория категорий, монады в хаскеле действительно дают какие-то бонусы благодаря соответствию определениям из теорий групп и категорий. Но вот в практической деятельности пользы от соответствия абстрактным теориям я не встречал. По частям, например для теории групп, могу себе представить пользу, но вот где все эти определения нужны в сумме — не знаю.

Ну а ссылки на повышенный уровень абстракции здесь, в общем-то, опять ничего не объясняют и лишь добавляют ещё больше тумана. Разработчику нужно знать, зачем нужны абстракции. Например — абстракция «список» — очень понятная, простая, с массой практических приложений. А вот абстракция «монада» — здесь я уступаю дорогу сторонникам ФП, может когда-нибудь и до них дойдёт, что их идеи без понимания практического смысла никто не поддержит.

Хотя это всё в итоге уйдёт в обсуждение темы «зачем нужна теория категорий», а потому даже в стандарте хаскеля пишут примерно так — теория категорий здесь не при делах, просто вот есть у нас такие вычурные названия и паттерны, и всё, пользуйте (если хотите).
Опять поверхностный наброс.

Монады введены в хаскель ради отложенной обработки сторонних эффектов, а это (внезапно) как раз и есть асинхронная операция. Что нужно для асинхронности? Сложить отложенный вызов в коробку и поставить на балкон ожидать, пока она там приготовится. Вот именно такая простейшая идея и лежит в основе монад — засовываем туда не исполнение, а информацию об исполнении. В ФП такой информацией является функция. Её отдают монаде (коробке на балконе) и забывают обо всём. Далее компилятор определяет момент, когда сложатся условия для вызова функции (то есть прячет от пользователя асинхронность, свешивая её на разработчика компилятора) и после наступления условий — вызывает функцию.

Теперь подумаем, а что нужно для императивной реализации показанной выше простейшей схемы? И оказывается (опять внезапно), что нужно просто прочитать первый абзац и реализовать его императивно. Но вместо функции передать объект такого типа, который реализует нужную функцию. В приличных императивных языках это давно делается передачей лямбда-функций. То есть от всей функциональщины полезной оказалась идея передачи функции в качестве параметра. И да, эта идея одобрена и широко используется в императивных языках. А вот монады, которые как мы видели, по сути есть коробки для тех же самых функций, во первых, нужны только в асинхронных случаях (и автор это сам доказал, приведя два примера одного и того же синхронного кода на хаскеле и на С#), а во вторых тривиальнейшим образом реализуются в приличных императивных языках, будь у разработчика для этого минимальное желание.

Ну и вывод. Из-за непонимания природы явления (монады) автор притянул за уши изоляцию асинхронности к славословию в адрес ФП. То есть славословие здесь совершенно неуместно, ибо монады есть лишь один из множества (и весьма кучерявый) способ управления асинхронностью. Кто хочет реализовать именно такой способ, тот делает это легко в рамках имеющихся возможностей императивных языков. А если же кто-то использует неподходящие способы реализации асинхронности, то автор совершенно зря выбирает такие примеры в качестве подтверждения своего славословия, ибо безумный код подтверждает лишь безумие кодировщика, а не какие-то преимущества ФП.
Суть серии статей, в общем-то, как всегда не техническая.

Суть примерно такая — функциональщики мало внимания уделяют реальному миру и много виртуальным алгоритмам. Сишники много возятся с реальностью (процессоры, регистры, железные оптимизации) и мало с виртуальностью (математика вообще). В итоге два мира смотрят друг на друга свысока, но всё же сишники несколько завидуют функциональщикам, ведь те реально демонстрируют знание каких-то разделов математики, хоть и мало полезных, но не изученных оппонентами. И вот эта неизученность гложет. А тут повод — можно заткнуть за пояс «этих математиков». Отсюда серия статей.

На самом деле обе стороны ничуть не лучше в плане интеллекта, но просто у них разная специализация. А математическая «продвинутость» (как кажется функциональщикам) есть лишь следствие необходимости изучать подаваемый именно с математической точки зрения материал о своих любимых ЯП. При этом абстрактные высоты, естественно, затмевают низкий уровень, начинает казаться, что там внизу одни негры делают грязную работу. Ну и далее очень быстро возникает желание поучать негров, например бенчмарками ФП vs С. Только в данном случае, так сказать, нашла коса на камень.

И урок из данной темы, на мой взгляд, стоит в первую очередь извлекать именно функциональщикам. Чисто психологический — не зазнавайтесь. А вот сишникам (и прочим сторонникам низкоуровневых подходов) всё же стоит интересоваться альтернативными подходами к программированию, просто что бы не на случайном удачном примере о производительности рассуждать, а (по возможности) на равных отметать высокомерные заявления некоторых функциональщиков именно на их территории. Но для этого нужно изучать чужие подходы. И как раз такое занятие не нравится ни функциональщикам, ни сишникам.

Нужно учиться понимать друг друга. Банально, но пока оба мира не интересуются друг другом, будет только вот такая «мягкая» война, когда неудачный пример одной стороны вызовет бурю ликования с другой (ну ладно, не ликования, а разумеется, взвешенного анализа, но с целью показать, что оппоненты слабаки и не увидели того, что на самом деле нужно было увидеть).

Разрыв между двумя лагерями вроде бы очевиден, но оба лагеря не хотят этот разрыв ликвидировать. И потому война (в мягком виде) продолжается. Поэтому стоит подумать именно о психологических причинах происходящего. Ну и написать статью «Почему С лучше Хаскеля», но за авторством функционального сообщества, а потом «почему Хаскель лучше С», но за авторством сообщества низкоуровневой разработки. То есть честно признать плюсы оппонентов. Как бы психологически трудно это не было.
Автор малость не понимает суть бухгалтерии, хоть и не он один такой (а таких на самом деле большинство), но всё же надо как-то от этого избавляться.

1) Бухгалтерия — это отчётность перед врагами.
2) Отчётность перед друзьями называется «Управленческий учёт».
3) Есть ещё аудиторы, но уровень, на котором используется 1С, не предполагает серьёзной отчётности именно средствами бухучёта.

Следствием указанных пунктов является ненужность собственно бухгалтерского подхода для понимания показателей работы предприятия. Задача (главного) бухгалтера — минимизировать штрафы от врагов. Как он этого добьётся — зависит от его грамотности. В общем случае штрафы минимизируются показом левой отчётности, а вот как конкретно доказать врагам, что отчётность «не левая» — вот здесь действительно нужно творчество.

Отсюда вывод — бросать на бухгалтерию много людей, чаще всего означает передачу бухгалтерии функции управленческого учёта. Этого не понимают многие финансовые директора и им подобные начальники над главными бухгалтерами. Когда управленческий учёт ведётся бухгалтером — все данные, помимо необходимости быть обфусцированными для врагов, дополнительно искажаются разного рода бухгалтерскими взглядами на жизнь. В итоге отчётность становится непрозрачной. А фин.директор часто просто не знает, как может быть по другому, вот и терпит. Ну и вышестоящее начальство тоже терпит. И имеет мутное понимание своего собственного бизнеса. А в результате складывается ситуация вроде описанной в статье.
Подход с автоматами работает лишь в малом, а в большом бесполезен. Да и в малом эффективность переключения потоков ограничивает минимальный размер автомата, а потому есть довольно узкая ниша, где такой подход применим, но из-за узости о нише имеет смысл думать лишь когда нужно на низком уровне заниматься оптимизацией, что опять таки бывает редко.

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

Вы, как настоящий Цукерберг, видите исключительно себя в роли выгодоприобретателя. Но суть общества в большом количестве участников. И все они хотят жить лучше. Ваша цель = забрать максимум у всех. Альтернативная цель — дать максимум всем. Поэтому ваш перепев, повторюсь, отражает лишь ваше исключительно эгоистичное восприятие себя в этом мире, а так же свидетельствует о полном неприятии вами каких-либо уступок (кроме как под нажимом) кому-то ещё.
>> Обсуждаем, кто предлагает их национализировать, и почему это не такая уж хорошая идея

Текст явно заказной.

Автор заявил, что покажет, «почему это не такая уж хорошая идея». И где? А нету!

Много слов ни о чём, плюс ряд фактов, по сути опровергающих заявление «это не такая уж хорошая идея». Рассуждения ради рассуждений, просто потому, что надо (заплатили).

На самом же деле проблема очень глубокая. Мы и так уже по сути рабы, а завтра этому ещё и официальный статус придумают. И всё именно потому, что вместо кооперативов нами правит кто попало. Ну и очевидное следствие — кому попало интересна в первую очередь их личная задница, а мы все им интересны исключительно в роли рабов, разве нет? Вы можете представить себе Цукерберга, отдающего рабам свой фейсбук? Сейчас много сторонников позиции «вот я когда-то обязательно стану цукербергом» выльют на меня бочку нечистот и заявят — а почему это любимый нами Марк должен тебе что-то отдать? И я с радостью их поддерживаю — ну совершенно нет ни малейшего основания ожидать от любимого вами Марка такого подарка! Просто потому, что мы для него — действительно рабы (dumb fucks, как он сам выразился).

Суть происходящего в мире простая — залезть на трон и не слезать, именно такую цель преследуют цукерберги и прочие борцы за «свободный рынок». А сам «свободный рынок» здесь лишь прикрытие их собственной «невидимой руки». Ну а кооперативы и всеобщее самоуправление — это очевидные враги цукербергов и прочих императоров, просто потому, что они отнимают у царя рабов. Значит цукербергам очень срочно нужно предпринимать меры, по уничтожению конкурентов. И они принимают — проплачивают вот такие вот статейки. Хотя, конечно, статейки, это лишь верхушка айсберга, а начинается он с очень примитивного выбора царями себе помощников, потом помощники выбирают холуёв уже себе, те снова для себя и так далее. А потом вся эта армия паразитов долго думает и много раз пробует поставить своих людей везде, в правительство, в прессу, в университеты. То есть все, кто хоть немного способен влиять на общество, становятся целью этих недоделанных императоров, ну и после набора критической массы, общество (наконец, ведь долго ждали!) становится рабом императоров. Что и произошло в реальности. Ну почти, пока ещё формально рабский статус не закреплён, но разве в формальностях дело? Зато есть миллион regulations, которые все без исключения плебеи обязаны выполнять. Это и есть главная цель — что бы приказы выполнялись. Хотя да, без формального порабощения кайф цукербергов будет неполным…

А теперь ждём бурную реакцию граждан типа «вот я когда-то обязательно стану цукербергом»!
>> компания старается тебя удержать и очень ценит

Ну вот, собственно, и вся характеристика интервьюируемого. Он считает, что его очень ценят в гугле. Ну что тут сказать?

Молодой парнишка, верит в гугл, верит и в другие глупости. Жизни не видел, сразу из института забрали на всё готовое. Далее он начинает творить. Не зная жизни, естественно. И что он натворит? Сбор данных пользователей, продажу данных пользователей, управление роботами, отстреливающими неугодных пользователей. Как-то так. Ведь он не вдаётся в детали происходящего. Ему сказали — сотвори техническую приблуду, ну и он рад поиздеваться над пользователями. Потому что пользователей он не видит, не знает и знать не хочет. Чистый технарь с математическим уклоном в сторону виртуальных алгоритмов. Идеальный робот.

С возрастом это проходит, но в гугле ведь умные ребятки работают, правильно? Вот они и набирают новых роботов на замену начинающим что-то понимать. Либо вариант номер два — на всю жизнь остаться роботом. Стоять в стойле и тащиться от количества баксов, вложенных в пустующий домище на сотни квадратных метров.

В общем — статья для начинающих роботов, про то, как вам стать стать «высокооплачиваемым» роботом.

Information

Rating
Does not participate
Registered
Activity