LINUX.ORG.RU

Алан Keй: deep flows in Lisp's logical foundations

 


1

5

I could hardly believe how beautiful and wonderful the idea of LISP was. I say it this way because LISP had not only been around enough to get some honest barnacles, but worse, there were deep flaws in its logical foundations. By this, I mean that the pure language was supposed to be based on functions, but its most important components — such as lambda expressions, quotes, and conds — were not functions at all, and instead were called special forms ... My next questions was, why on Earth call it a functional language? Why not just base everything on FEXPRs and force evaluation on the receiving side when needed? I could never get a good answer

Далее Кей пишет о том, что эти размышления привели его к идее создания модели Ъ-ООП

А, все же интересно, кто-нибудь, таки, дал ответ на вопрос Алана Кея?

Ответ на: комментарий от komputikisto

на момент разработки стандарта

Ещё гигиена уже была (а значит привязки), AFAIK.

Но я не про стандарт. В стандарте и TCO нет (в SBCL и вообще почти везде он есть) и место определения при компиляции функции запоминаться не должно (а без этого не работал бы SLIME). На сегодня нет ни одной реализации CL с call/cc!

но я не думаю, что точно этими соображениями руководствовались разработчики CL

А по какой ещё причине разработчики современных компиляторов CL принципиально не хотят включать в них call/cc?

Из-за соображений компиляции, видимо, все циклы в CL раскрываются в go + tagbody.

Из-за того, что всё должно раскрываться в специальные формы. Среди которых для циклов ничего кроме go нет.

Последнее один-в-один компилируется если не учитывать продолжения

А если учитывать? В C ведь есть функция longjmp. И она ничуть не мешает всему коду компилироваться один-в-один в ассемблер.

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

Из-за того, что всё должно раскрываться в специальные формы.

Циклы можно было бы раскрывать в рекурсию, если была бы реализована оптимизация хвостового вызова. Еще за базис можно взять block + return-from + redo. Так почему же они взяли за базис tagbody + go? Единственный возможный ответ — наследие машинных языков и компиляторов того времени.

В C ведь есть функция longjmp.

Которая не реализует полные продолжения, а только escape continuations. Компиляция в присутствии продолжений — уже не один-в-один преобразование. Меня лично компиляция не интересует, но тем кто не равнодушен к этой тематике, могу посоветовать книгу «Compiling with Continuations» Andrew Appel.

принципиально не хотят включать в них call/cc?

Так это надо у них спросить. Я лишь утверждаю, что это историческая проблема и никакого отношения к философии CL не имеющая.

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

Еще за базис можно взять block + return-from + redo

Про рекурсию ты сам ответил. redo — это что?

Которая не реализует полные продолжения, а только escape continuations.

Да ну?

#include <stdio.h>
#include <setjmp.h>

jmp_buf env;
int i;

void foo() {
    i = 1;
    setjmp(env);
    i++;
    printf("%i\n", i);
}

int main()
{
    int j = 0;
    foo();
    if(j<3) {
      j++;
      longjmp(env, 0);
    }
}

работает и выводит

2
3
4
5

Так что продолжение foo вызвалось трижды.

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

Ты на самом деле верно описал философские различия межу CL и Scheme. Я лишь хочу уточнить, что есть глубинное деление, а что просто историческое недоразумение. Отсутствие first-class продолжений — просто наследие устаревшего подхода, в который вцепились некоторые разработчики. И все!

Что до самой сути тезиса того поста, то это все описывается в рамках дихотомии подход MIT/подход Unix. Именно поэтому Scheme вырывается в лидеры, т.к. подход Unix всегда в этом мире побеждает подход MIT. Поэтому у CL меньше разработчиков и замедленный прогресс.

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

Я лишь утверждаю, что это историческая проблема и никакого отношения к философии CL не имеющая.

Была бы историческая, её бы уже давно решили. Прямому использованию longjmp мешает только GC. Но все, кто мог бы её решить, отвечают «не нужно». :-)

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

setjmp() не копирует стек, longjmp() вверх по стеку приводит к UB, и приведённый выше пример — это тоже UB, так как нельзя делать longjmp() после того, как функция, вызвавшая setjmp(), вернула значение.

Но вообще есть ещё getcontext(), setcontext(), makecontext() и swapcontext(), которые как раз таки сохраняют стек и позволяют устроить полноценные продолжения.

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

Отсутствие first-class продолжений — просто наследие устаревшего подхода, в который вцепились некоторые разработчики.

Ну так я в философских различиях и не писал про продолжения. Писал про «все сложные концепции». Могу ещё вспомнить asdf vs xcvb. Несколько попыток сделать иерархические пакеты и переопределение имён (например, https://github.com/Kalimehtar/advanced-readtable). Cl-containers vs https://github.com/fare/lisp-interface-library.

Именно поэтому Scheme вырывается в лидеры, т.к. подход Unix всегда в этом мире побеждает подход MIT.

??? Смотрю https://ru.wikipedia.org/wiki/Чем_хуже,_тем_лучше

И вижу в CL подход UNIX:

  • Простота — самое важное требование при выборе дизайна.: поэтому везде символы вместо более сложных объектов, плоская пакетная система и никаких сложных концепций
  • Простой дизайн немного лучше, чем правильный. Опять же про макросы: syntax-case правильней, defmacro проще.
  • Иногда можно пожертвовать логичностью ради простоты. Дизайн пакета CL предоставляет кучу примеров.
  • Полнотой можно жертвовать в пользу остальных качеств и обязательно нужно жертвовать, если она мешает простоте. : пример сходу не подберу, но в целом краевые случаи в CL проверять не принято

А в Racket, наоборот, подход MIT

  • Простота интерфейса важнее простоты реализации : поэтому никаких docstring'ов в реализации. Только отдельная нормальная документация
  • Правильность: дизайн должен быть правильным во всех отношениях. Неправильный дизайн категорически запрещён : поэтому на всех экспортируемых функциях контракты (причём неотключаемые), поэтому аналог (safety 0) вообще отсутствует.
  • Логичность так же важна, как и правильность. Ради логичности можно жертвовать простотой и полнотой. : поэтому синтаксические объекты и гигиена, модули и юниты.
  • Полнота: дизайн должен охватывать как можно больше важных ситуаций. Все вероятные ситуации должны быть предусмотрены. Простота не должна слишком мешать полноте. : опять же контракты и отдельная документация, описывающая поведение во всех ситуациях.
monk ★★★★★
()
Ответ на: комментарий от komputikisto

Переход на начало block.

Так чтобы перейти на начало придётся сделать go. Иначе никак. Среди специальных форм redo нет.

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

longjmp() вверх по стеку приводит к UB

Да, недочитал :-(

есть ещё getcontext(), setcontext(), makecontext() и swapcontext()

Причём ситуация аналогична CL. В ANSI C их нет, но в POSIX добавлены. Поэтому ссылаться на то, что «в момент принятия стандарта этого не было» не надо.

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

логичность правильность

Ага:

(define (a) 1)
(define (alpha) (a))
(alpha)             ;  1
(define (a) -1)
(alpha)             ; -1

(define-syntax b (syntax-rules () ((_) 2)))
(define (beta) (b))
(beta)              ;  2
(define-syntax b (syntax-rules () ((_) -2)))
(beta)              ;  2 still!

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

Ага

Всё логично. Определение beta раскрывается в (define (beta) 2) и никак не может зависеть от переопределения синтаксиса. К слову, эта логика и в CL работает.

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

http://www.reduce-algebra.com

Я даже застал момент когда оно closed source было, и писал разработчикам нету ли скидок для бедных студентов (по какойто случайности через месяц после этого reduce открыли).

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

http://www.reduce-algebra.com

Я даже застал момент когда оно closed source было, и писал разработчикам нету ли скидок для бедных студентов (по какойто случайности через месяц после этого reduce открыли).

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

Всё логично

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

Для сравнения newlisp:


(define-macro (b) 2)
(define (beta_) (b))
(beta_) ;  2
(define-macro (b) -2)
(beta_) ; -2              

тут все не логично и неправильно, конечно же.

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

Среди специальных форм redo нет.

Мы же говорим о дизайне языка. Такая спец. форма могла бы быть и тогда не нужна была бы tagbody. Полный go все равно вообще никем не используется, обычно все циклами пишут. А вообще нужны только продолжения и никаких циклов tagbody, block и прочего.

И вижу в CL подход UNIX

Ты придираешься к мелочам. Интересно, что реализация Scheme из той самой конторы (MIT Scheme) наиболее похожа на CL. Там есть рестарты, обобщенные функции (т.е. основные фичи CL, которые и составляют его суть). CL не может быть идеальным, мне он очень не нравится, но всякий Лисп, который игнорирует рестарты и CLOS - игнорирует философию Лиспа. То, на что ты обращаешь внимание - недостатки конкретного дизайна, но не философии.

Если оставить в стороне конкретные фичи, то MIT Лисп создает инфраструктуру на своей основе (Genera как пример), пользователь — программист (и профессиональный программист), нет категории ошибок (рестарты), документация — часть языка и системы исполнения, работает в образе.

Unix Лисп пользуется инфраструктурой в которой живет. Пользователь — не программист. Есть понятие отдельной программы, отдельной документации, контракты, если так уж хотите.

CL не в чистом виде MIT Лисп, но он близок. Настоящий MIT Лисп был бы создан если бы не AI winter.

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

Похоже, именно это имел в виду Алан Кей, когда говорил об экстрмально позднем связывании

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

тут все не логично и неправильно, конечно же

Да. Тут одно из двух: или define-macro на самом деле определяет ленивую функцию, а не макрос, или при каждом вызове функции происходит перекомпиляция её тела. И то и то не логично. В первом варианте некорректно название define-macro, во втором — семантика языка.

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

CL не может быть идеальным, мне он очень не нравится, но всякий Лисп, который игнорирует рестарты и CLOS - игнорирует философию Лиспа. То, на что ты обращаешь внимание - недостатки конкретного дизайна, но не философии.

Рестарты при наличии call/cc не нужны. CLOS — в мире Scheme с ним та же проблема, что у xcvb в мире CL. Есть уйма реализация CLOS: swindle, fast-generics, gls. Но их (практически) не используют, так как их использование нарушает модульность.

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

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

В первом варианте некорректно название define-macro

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

\\thread

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

Рестарты при наличии call/cc не нужны.

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

CLOS и рестарты родились как раз как результат философии

Так я о чем и горою. Это подход MIT, подход Unix в модульности.

komputikisto
()

terminator-101

На рутрекере появилась раздача: «Hardcore Functional Programming in JavaScript».

Там про Monads, Functors, Monoids and Applicatives и всё такое, тебе, возможно, будет интересно :)

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

Так я о чем и горою. Это подход MIT, подход Unix в модульности.

Врешь. Тебе верно сказали, что схема — это MIT-way. Но и CL недалеко ушел (читай о простоте). Оба поделия из митовского говна сделан. Unix когда то чем-то напоминал Нью-Джерси-стайл, все есть текст, как единый интерфейс, KISS, бла-бла-бла, но сейчас это уже история, сейчас UNIX — это MIT/windows.

Также и лисп-фанбои упрямо твердят базворды, code=data. Домохозяйки им верят, а сами они не имеют представления, что это такое. Как только появляется компиляция, эквивалентность кода и программы исчезает.

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

First-class продолжения невероятно сложно компилировать, причем в особенности компилировать эффективно. Они заставляют отказаться от «машинного» стека вообще совсем, они просто нивелируют помощь бренч-предиктора в процессоре, и т.д.

Но даже это не важно. Самая основная проблема с ними в том, что они дают слишком много свободы пользователю, фактически нивелируя всю их мощь. То есть, они позволяют как построить любую конструкцию вычисления, так и обойти ее, причем особо не напрягаясь. Тот же unwind-protect с помощью полноценных продолжений реализовать нельзя хотя бы потому, что любой использующий продолжения код гарантии unwind-protect сломает. Чтобы это обойти, в том же racket существует далеко не один костыль, всякие там ограничения на блок, и тому подобное, но они фактически нивелируют мощь полноценных продолжений, и собственно делая их ненужными.

Наиболее полезная и мощная модель операционной семантики языков программирования с аппликативным подходом к вычислению(не ленивых, т.е.) на текущий момент сделана в CL. Т.е. это escaping continuations.

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

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

Как только появляется компиляция, эквивалентность кода и программы исчезает.

Да, компилируемый Лисп — уже не Лисп, если речь, конечно, не идет о компиляции как частичном вычислении интерпретатора. Более того, она исчезает в момент выполнения (интерпретации). Программа != выполнение. Поэтому, в я думаю, что нужен еще один термин, чтобы отразить тот факт, что объекты, которые являются реперзентацией поведения = данные. Это не так, например, в Scheme — континуация есть функция и ничего кроме вызова с ней не сделать. Ей нельзя оперировать как данными, т.е. нельзя разложить на составляющие (на фреймы, например) и собрать заново. В Scheme нельзя сделать новый тип континуаций. Я предлагаю такой термин — поведенческая гомоиконность.

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

Ей нельзя оперировать как данными

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

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

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

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

сейчас UNIX — это MIT/windows.

Вот этот slash между MIT и windows говорит о том, что в твоей голове все смешалось. Чем принципиально отличаются системы Bell Labs от MIT'овских? Unix от Genera и CADR-машины? Философия unix — делай одну вещь за раз. В Unix много разных отдельных программок для различных нужд. Текст как единый интерфейс — следствие того, что эти маленькие программки нужно как-то соединять и заставлять работать вместе. Подход MIT в другом. Давайте посмотрим на конкретные системы спроектированные в MIT — Genera, CADR-машина. Суть этих систем не в простоте и не в едином интерфейсе, а в том, что нет никаких отдельных программок, пользователь — программист, который дописывает ОС. Если мыслить в категориях Unix — программы в Genera составляют часть ОС. На самом деле это лишь аналогия, т.к. там нет понятия программы и ОС в привычном смысле.

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

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

Залогинься, убогий.

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

в некотором смысле, ей можно оперировать как данными

Могло бы быть больше. Например, тут предлагается другой интерфейс к продолжениям (нефункциональный). Именно к Лиспам с таким интерфейсом к продолжениям я собираюсь применять термин поведенческая гомоиконность. В Kernel, вообще можно создавать новые продолжения при помощи функции extend-continuation. Лисп с поведенческой гомоиконностью также должен предоставлять интерфейс к first-class окружениям. Не нравится термин? Жду других предложений.

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

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

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

философия юникс — простота рализации + срать на интерфейс. МИТ - строго наоборот.

Так вот. Вернемся к первоначальному разговору. CL — подход MIT, а Scheme — подход Unix.

накидай прототип, а потом допиливай

Это как раз про Схему. С ней происходит вся та же история, что с Unix. Начинали с того, что сделали простой прототип языка, потом в процессе допиливания она распадается на множество диалектов, а стандарт обрастает костылями. У CL другая история. Пытались сделать идеальный Лисп, который всех бы устроил, но фейл.

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

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

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

Чтобы это обойти, в том же racket существует далеко не один костыль

Нет там костылей, да и обходить это не собираются. Библиотечный код юзает continuation barriers и все збс. Чистота интерфейса важнее ремней безопасности.

Наиболее полезная и мощная модель операционной семантики языков программирования с аппликативным подходом к вычислению(не ленивых, т.е.) на текущий момент сделана в CL. Т.е. это escaping continuations.

Пруфы или ты граф Толстой.

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

Оба диалекта, и схема и CL — Пример митовского подхода. Оба языка переусложненные, оба уродливы и ограничены. Изначально схема декларировала минимализм. Но декларация так и осталась декларацией. К тому же, минимализм ради минимализма — тоже плохая идея.

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

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

С ней происходила другая история. Краткий исторический экскурс. Жил-был такой недоумок — Гай Стилл. Он нес постоянно форменный бред, но с умным видом. Сначала он сказал, что рекурсию можно сделать быстрой и не жрущей память, если разворачивать ее в цикл. Для этого ему понадобился лексический скоп. По хрену, что лексический скоп убивает 90% выразительности, главное, что мы можем рекурсию разворачивать в цикл. Когда его спрашивали, нах*я товарищь дебил, городить рекурсию, чтобы ее потом разворачивать в цикл, если можно сразу написать цикл, он недовольно мычал: «а что мне еще делать, мне ж надо что то пейсать, а если у меня мозга нет, дайте мне хотя бы подрачить на мои бредни». Одновременно с этим, он своей тупой головкой пытался понять акторы. Акторы он так и не понял, но уродливое детище таки появилось — это была схема. А чуть позже — Великая Жаба. ИЧСХ, это мудило до сих пор выступает с лекциями, и горячо любит жабское ООП с класами (ну а как же — лексические замыкания рулят, епт)

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

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

Поддерживаю. Все вот эти заявления просто тормозят развитие Lisp как самого продвинутого языка программирования. Lisp — свобода, а для людей нуждающихся в наручниках и намордниках есть другие языки. Все эти аргументы против континуаций и fexpr'ов мы слышали в споре «статическая или динамическая типизации».

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

По хрену, что лексический скоп убивает 90% выразительности

Я так понимаю, еще один поклонник dynamic scope или это terminator-101 забыл залогиниться? А решение с комбинацией dynamic/lexical scope вас не устроит? Lexical по умолчанию, а dynamic через спец. форму.

горячо любит жабское ООП

Вот этого я в его личности не могу понять. Я думаю разгадка проста — деньги.

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

Lexical по умолчанию, а dynamic через спец. форму.

Нет, должно быть наоборот.

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

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

Ну т.е. ты не понял. Ок, уточняю:

То есть, они позволяют как построить любую конструкцию вычисления, так и обойти ее, причем особо не напрягаясь.

Причем даже не подозревая об этом.

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

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

а призывать высшие силы ради его убийства ты не стал? :)

Да какой с него спрос...

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

Нет, ты в данном случае не понял, что я хотел сказать.

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

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

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

Т.е. тут вопрос не в тупости программистов, а в том что невозможно предугадать все паттерны использования продолжений, и что самое главное, их взаимодействие друг с другом. В частности поэтому в CL пошли по пути выделения паттернов, и фиксации их в семантике языка(и получилось очень хорошо - ядро языка отлично продумано как я уже писал).

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

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

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

В противном случае это просто то же тупое goto, оверюз которого - considered harmful и все такое.

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

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

Для разрешения этих проблем были придуманы delimited continuations. Кто нибудь знает, кстати, как на русский перевести?

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