LINUX.ORG.RU

Promises в Scheme и других ЯП

 , ,


0

4

Добрый день!

Пишу сейчас на коленке имплементацию Scheme (r5rs) на Java (да, полностью r5rs на джаве не получится реализовать, но это и не планируется).
Немного запутался в delayed evaluation'ах и в promises.

Если открыть, например:
http://community.schemewiki.org/?delayed-computation

то там пишут:


A promise is, quite simply, the promise to evaluate an expression later.
It is not evaluated until explicitly requested, although R5RS permits promises to be evaluated implicitly when passed to primitive operations and their result be used instead.


Тоже самое пишут и на:
https://docs.racket-lang.org/reference/Delayed_Evaluation.html


A promise encapsulates an expression to be evaluated on demand via force.
After a promise has been forced, every later force of the promise produces the same result.



Т.е. promise - это просто выражение, которое мы вычислим когда-нибудь потом, либо явно через force, либо неявно.
Результат запоминается после вычисления и повтороное вычисление promise'а всегда возвращает сохраненный результат.

API для promise'в довольно простой: delay, да force.
И разные вспомогательные promise?, promise-forced?, promise-running?

Если же посмотреть promises в JavaScript или Java:
https://www.promisejs.org/implementing
https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFut...

То, как я понимаю, речь немного о другом.
Future - read-only 'контейнер' для результата будущих вычислений
Promise - writable 'контейнер' для результата будущих вычислений

Т.е. допустим, мы создаем Promise и передаем его куда-нибудь, обещая положить туда результат неких вычислений когда-то в будущем.
На той стороне (обычно, в другом треде) получают созданный нами promise и в нужный момент пытаются получить его результат (блокирующий вызов).
API намного сложнее.

Как я это вижу:
в Java/JS/... - это concurrency примитив, 'контейнер' для результатов (часть методов которого - блокирующие)
в Scheme promise - это control feature, которая просто позволяет делать delayed evaluation

Так ли это?

Второй вопрос:
Будет ли в Scheme какая-то принципиальная разница (кроме memoization) между promise и обычным s-expr?

(force (delay (+ 1 2))) ; => 3
(eval  (quote (+ 1 2))) ; => 3

Т.е. должен ли я использовать Java'вский CompletableFuture (aka Promise) для реализации Scheme Promise
или достаточно будет создать обычный класс Promise, который просто хранит тело выражения (s-exp) и результат (если есть), а force просто берет тело и выполняет его (передает хранящийся в promise s-exp evaluator'у)?

★★★★★

Последнее исправление: kovrik (всего исправлений: 3)
Ответ на: комментарий от monk

Оба шага внутри одной функции (withdraw). Клиент её только вызывает.

но тогда откуда на диаграмме появляется «новое значение: 100-25=75». Нахрена считать новое значение, если мы не знаем текущее?

filequest
()
Ответ на: комментарий от filequest

но тогда откуда на диаграмме появляется «новое значение: 100-25=75». Нахрена считать новое значение, если мы не знаем текущее?

В смысле? Текущее мы читаем из бд (или переменной в примере SICP).

процедура снять_деньги(сколько):
  был_остаток := прочитать_из_бд_баланс()
  новое_значение = был_остаток - сколько
  записать_в_бд_баланс(новое_значение)
конецпроцедуры
monk ★★★★★
()
Ответ на: комментарий от filequest

можно сделать несколько экземпляров, а затем смержить.

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

monk ★★★★★
()
Ответ на: комментарий от monk

То есть твой банк может обслуживать клиентов (позволять им снимать деньги) только по очереди? Ну-ну... расскажи ещё про правильное проектирование

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

filequest
()
Ответ на: комментарий от monk

был_остаток

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

filequest
()
Ответ на: комментарий от monk

Короче говоря, счет должен быть одним потоком, на время выполнения процедуры проверки и изменения счета, код должен выполняться последовательно. Это очевидное решение. Даже в реальных банковских системах это не вызовет никаких проблем, и серьезных задержек, количество обращений к одному и тому же счету 2-х и более клиентов ничтожно. В противном случае, проблемы неизбежны.

filequest
()
Ответ на: комментарий от filequest

неразрешимая задача

Оптимистическая блокировка, пессимистическая блокировка.

winlook38 ★★
()
Ответ на: комментарий от filequest

код должен выполняться последовательно

Этого и добиваются, устраняя lost update problem.

Даже в реальных банковских системах это не вызовет никаких проблем, и серьезных задержек, количество обращений к одному и тому же счету 2-х и более клиентов ничтожно.
ничтожно

В реальных банковских системах есть ЗОД, который говорит нам, что твои представления о реальных системах ничтожны. Более того, Счет - не единственная сущность, к которой осуществляется конкурентный доступ. И нужно понимать суть происходящего, чтобы выбирать правильную стратегию в том или ином случае.

winlook38 ★★
()
Ответ на: комментарий от winlook38

Я с твоим тупняком разговаривать не собираюсь, расслабься

filequest
()
Ответ на: комментарий от Legioner

тут имеется в виду, не тот implicity который ты думаешь. Это о самих примитивах force и delay, вот полностью:

R5RS permits promises to be evaluated implicitly when passed to primitive operations and their result be used instead. R5RS provides two primitives for lazy evaluation, the syntax delay and the procedure force.

это не отменяет того, что ты должен явно писать delay force, то же самое что и eval, например. В ленивых функциях другая семантика, там аргументы вообще никогда не вычисляются.

filequest
()
Ответ на: комментарий от filequest

У них там баланс меняют клиенты, а это должен делать банк

Должен? Кому должен? А звук при ударе палочки о барабан кто производит - палочка или барабан?

loz ★★★★★
()
Ответ на: комментарий от filequest

Никто изначально не собирался совать схему в браузер. У Айка изначально была задача создать язык с си-подобным синтаксисом

rly? а если я найду цитату из его интервью - ты обещаешь больше не писать на всех ООП языках?

loz ★★★★★
()
Ответ на: комментарий от filequest

Макросы «есть» в любом языке, пиши препроцессор и будут тебе макросы

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

loz ★★★★★
()
Ответ на: комментарий от filequest

Эти две процедуры выполняются в одном потоке(при правильном проектировании) — в потоке банка

Лол, однопоточные банки, расскажи еще сказок.

loz ★★★★★
()
Ответ на: комментарий от loz

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

В данном случае, такую гарантию дает инкапсуляция в Ъ-смысле: только банк(счет) имеет доступ к своей памяти, никакие клиенты ничего там менять не могут, они могут только обратится к банку, который это сделает.

filequest
()
Ответ на: комментарий от loz

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

filequest
()
Ответ на: комментарий от loz

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

filequest
()
Ответ на: комментарий от loz

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

filequest
()
Ответ на: комментарий от loz

тут даже дело не столько в однопоточности счета. просто если мы исходим из задачи синхронизации этих 2-х процедур, нам важно, чтобы транзакция не привела, например, к отрицательному счету, в этом контексте. Проверка и изменение счета должны происходить синхронно, остальное вообще не важно.

filequest
()
Ответ на: комментарий от anonymous

это никчемный пример, который вводит в заблуждение. Авторы выстраивают кривую архитектуру, а затем валят все на присваивание. Бобина не виновата, если долб*б сидит в кабине, народная мудрость(с).

filequest
()
Ответ на: комментарий от filequest

Свой лучше, он заточен под конкретную задачу, более гибок и прост.

Препроцессорные макры убоги :-) Доказано на примере C и в C++ :-) Объяснить разумному, а не тупому троллю, это просто :-) Вот смотри, язык макр в Лиспе - это тот же Лисп :-) И с помощью этого же Лиспа можно делать всё что угодно, только на стадии компиляции - т.е. на стадии компиляции доступен весь Лисп :-) Ты же со своим убогим препроцессором вынужден будешь придумать убожество, которое будет тупо подставлять текст на хост-языке :-) Что это значит? :-) Это значит, что в ущербных язычках типа цепепе невозможно даже банально строчку на этапе компиляции сгенерить :-) Т.е. препроцессорные макры - отстоище :-) Но тупые тролли, конечно, этого не разумеют :-) Жаль их :-)

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

Хахаха :-)

anonymous
()
Ответ на: комментарий от Legioner

Ну через eval можно поизвращаться.

А, ну если это твоё кредо - «поизвращаться» - то дерзай :-)

Вообще макросы, eval — практика показала, что это всё не нужно.

Чья практика, твоя? :-) Ну зачем тебе, ищущему смысл, какие-то там макры? :-)

В современных промышленных языках это не используется.

В современных условиях редкие люди обладают разумом :-)

anonymous
()
Ответ на: комментарий от anonymous

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

filequest
()
Ответ на: комментарий от anonymous

В современных условиях редкие люди обладают разумом

Это правда. Когда образуется недовольство, оно затыкается суррогатами, происходит подмена понятий. Вместо Ъ лиспов — cl и scheme (а порой даже хашкель), вместо ООП — жабу и решетку, вот так и живем.

filequest
()
Ответ на: комментарий от filequest

у тебя язык макр это не лисп, это язык конфигурации препроцессора, просто он синтаксически похож на лисп.

Ты бредишь, да? :-) В CL язык макр - это CL :-)

(defmacro m ()
  (list 'format t "string from macro"))

Ты мог бы расширять не только целевой язык, но и язык макров(то есть, в твоем случае define-syntax — вот это вот все).

Мог бы :-)

Ты к препроцессору доступа не имеешь.

В CL имею доступ к ридеру и могу из CL хоть C++ сделать :-)

Ну, а основной отсос макросов вообще, конечно в том, что их нет в рантайме.

Да ты что? :-) Т.е. read в CL не может из файла взять определение defmacro? :-) Ну ты прям удивляешь :-)

anonymous
()
Ответ на: комментарий от anonymous

В CL язык макр - это CL

Я не о том языке, я о программном коде реализующем препроцессор, то есть макроэкспандере, или как там его у вас.

filequest
()
Ответ на: комментарий от filequest

Вот вот. Язык не виноват, если кодер *намек наверно все уловили* долб*б. Кто то выше верно подметил что ты ничего не понял читая эту книгу.

kardapoltsev ★★★★★
()
Ответ на: комментарий от kardapoltsev

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

filequest
()
Ответ на: комментарий от filequest

Я не о том языке, я о программном коде реализующем препроцессор, то есть макроэкспандере, или как там его у вас.

Причём тут макроэкспандер или как там его у них? :-) В defmacro доступен весь CL, весь его рантайм, в отличии от препроцессорного самопального гомна :-) Ты уж извини за грубость :-) Лол :-)

anonymous
()
Ответ на: комментарий от filequest

ридер читает только то что внутри макроконструкций

Как-то ты мыслишь узковато, надо бы масштабней :-) Ридер читает только то, что ему подсовывают :-)

и только на этапе компиляции.

Да, и в Лиспе этот факт никакого ограничения не накладывает, ибо в Лиспе, т.е. в Common Lisp, компилятор доступен в рантайме, если ты не знал :-) Лол :-)

anonymous
()
Ответ на: комментарий от filequest

на него возложили задачу по реализации ООП - языка, похожего на жабу, синтаксически

Ну так, на него возложили, а хотел и собирался делать по-другому.

loz ★★★★★
()
Ответ на: комментарий от filequest

Задача простая - есть класс палочка, класс барабан, куда вынести метод создающий звук? Так, чтобы нормально работало наследование, естественно. Колебания мембраны тут явно оверкилл.

loz ★★★★★
()
Ответ на: комментарий от anonymous

везде где есть eval компилятор доступен в рантайме, по второму кругу пошли, проблема в том, что код в рантайме не доступен.

filequest
()
Ответ на: комментарий от filequest

Я не о том языке, я о программном коде реализующем препроцессор, то есть макроэкспандере, или как там его у вас.

Это зависит от реализации, конечно, но в большинстве, я уверен он написан на CL.

loz ★★★★★
()
Ответ на: комментарий от loz

а кто он такой, чтобы решать как делать? мало ли что кто хотел?:)

filequest
()
Ответ на: комментарий от filequest

ридер читает только то что внутри макроконструкций, и только на этапе компиляции.

Ридер-макросы на этапе чтения, например.

loz ★★★★★
()
Ответ на: комментарий от loz

Зависит от задачи. колебания мембраны не всегда оверкилл. Если нам вообще не важна причинно-следственная связь, то можно не методом, а процедурой реализовать. Либо в обоих методы. Либо унаследовать обоих от класса Soundable. Без конкретной задачи трудно сказать.

filequest
()
Ответ на: комментарий от loz

Задача простая - есть класс палочка, класс барабан, куда вынести метод создающий звук?

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

anonymous
()
Ответ на: комментарий от loz

Ридер-макросы на этапе чтения, например.

Этап чтения я считаю частью компилтайма, тут не жто важно, а то что скомпилированный код им не подвластен

filequest
()
Ответ на: комментарий от filequest

проблема в том, что код в рантайме не доступен

Вобще не должно быть сложно это сделать, добавить поле к объекту функции и доступ через что-то типа function-source. Или даже в юзерспейсе сделать хешик символ -> определение, и написать макрос внутри которого все определения ложить в него. Может попробую на досуге.

loz ★★★★★
()
Ответ на: комментарий от filequest

везде где есть eval компилятор доступен в рантайме

Нет, в основном, везде, где есть eval, доступен тормозной интерпретатор, а не компилятор :-) Если кому-то интересны компиляторы в тормозной байт код, то мне нет :-) Мне нативный код подавай :-) Если eval твоего самопального препроцессора не умеет генерировать нативный код, то оставь его себе на локалхосте :-)

по второму кругу пошли, проблема в том, что код в рантайме не доступен.

Зачем в рантайме код? :-) Что такое код? :-) Текстик в фаееле? :-) Ну если ты любишь работать с абстракцией «текст в фаееле», то это твоя проблема :-) А лисперам доступна лисп-система, в которой «текст в фаееле» распрарсен и преобразован в объект лисп-системы :-)

anonymous
()
Ответ на: комментарий от anonymous

Что ты несешь? то что в сраных неООП язычках является процедурой, в ООП является таким же сообщением, это отправка сообщения глобальному объекту. А мультиметоды твои — говно, в Io реализуется в несколько строк, но даром никому не нужны.

filequest
()
Ответ на: комментарий от filequest

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

Мой ридер твою высокую мысль не распарсил :-)

А мультиметоды твои — говно, в Io реализуется в несколько строк, но даром никому не нужны.

Может быть, но ведь и Io никому даром не нужен, вот ведь в чём дело то :-(

anonymous
()
Ответ на: комментарий от anonymous

доступен тормозной интерпретатор, а не компилятор

Все JIT компиляторы являются интерпретаторами по-сути дела, что лисповский, что джаваскриптовский. То во что компилируется код, это вопрос десятый. JS сейчас может компилироваться в нейтив. Но это уход от темы.

А лисперам доступна лисп-система

это неправильные лисперы, они еще, я слышал, носят стринги, и красят губы. А олдскульные лисперы именно это и ценили.

filequest
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.