LINUX.ORG.RU

Какашки в Common Lisp


7

4

Предлагаю учёным мужам в этом топике собрать и обсудить проблемы в языке Common Lisp. Кому что не нравится?

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

Не нравится неполная интеграция CLOS в язык: распознавание класса в CLOS для стандартных лисповских типов ещё работает, но не для своих типов, объявленных через deftype.

Ну и вообще CLOS жирноват для 90% задач. Не говоря уж про MOP, который почти никем не используется, а если и используется, то для решения проблем с кривостями CLOS, либо просто книжку AMOP обчитался и повредился умом.

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

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

loop - какашка. Это не лисп. Точка.

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

unwind-protect - хорошо, но от попыток человеком сэмулировать продолжения для CL хочется икать. Такие trade-off вполне понятны, но лучше бы unwind-protect ограничили.

Ну и более мелкие ляпы в стандарте, типа (elt sequence index), но (nth index list).

Да, этот пост написан в Емаксе, запущенно под лисповым оконным менеджером человеком, получающем деньги за написание лиспокода :)

★★★★★

>Да, этот пост написан в Емаксе, запущенно под лисповым оконным менеджером человеком, получающем деньги за написание лиспокода :)

...
хочешь империии -
хватит постить,
создай свой ЯП,
умри забытым.
...

(с) перевразированный ЮЮШ

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

Да ты сцуко еретик.

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

mv ★★★★★
() автор топика

> Букв в общепринятой латинице и так мало (опустим перечень адских пыток, которым после смерти будут подвергнуты те, кто пишет комментарии не на английском языке или вообще использует не-ASCII алфавит для идентификаторов)

а мне так понравилось китайские иероглифы для идентификаторов использовать,

что теперь делать, прям не знаю

Harald ★★★★★
()

Я к лиспу вообще слабое отношение имею, но заинтересовался тут Scheme
а именно Chicken'ом. Как он в сравнении с CL?

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

>Лиспосрачи что-то затухли, выдохлись. Фундаментально идея лиспа непоколебима, конечно, но вот к реализации можно придраться.

Ну как же тут лиспосрач-то получится? Сейчас придет Кука, прочтет псто и напишет «Ну! Я же говорил!». :)

Zubok ★★★★★
()

Ну-у-у... Дядь, с одной стороны крайне хорошо, что ты вернулся, а с другой — ну разве же получится так срач?

sanuda
()

>loop - какашка. Это не лисп. Точка

А по-моему наоборот, очень даже ничего. Некоторых возможностей, правда, не хватает, например :collecting () не в список, а в вектор; но в малом количестве скобочек я не вижу минусов

Ну и более мелкие ляпы в стандарте, типа (elt sequence index), но (nth index list).

А ещё #'first, #'second и же с ними работают только со списками :(

В CLOS можно наследоваться от встроенных неюзверьских типов (отличных от t)? А то помню, что когда изучал, меня там что-то смутило, но сейчас уже не помню, что именно

yoghurt ★★★★★
()

>Мне категорически не нравится реализация методов в CLOS. Метод в нём - это специализированная общая функция (дженерик), просто функций, присущих только данному классу, нет.

Нормальная реализация, одна из лучших реализаций мультиметодов вообще.
Просто парадигма отлична от message passing, и если натягивать последний на CLOS «влоб», получается, естественно, говно.

При создании метода автоматически создаётся дженерик, если он ещё не был создан.


Почти все реализации выдают предупреждение.

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


Это вот из-за натягивания message-passing на CLOS.

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


Если методы текущей GF выполняют принципиально разные функции - почему бы и нет. Можно даже имена им разные дать.
Вообще говоря, &key аргументы у методов не обязаны соответствовать - у одного метода могут быть одни ключевые параметры, у другого - другие. Основные параметры, и &optional, вроде - да, их должно быть в сигнатурах одинаковое количество. Но, надо понимать, что аргументы диспетчеризации в CLOS это не «параметры функции», это аргументы диспетчеризации, то есть «объекты, которые кооперируют».

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


Говно можно на любом языке написать, но кстати, про это подробнее внизу напишу, про unwind-protect.

Не нравится неполная интеграция CLOS в язык: распознавание класса в CLOS для стандартных лисповских типов ещё работает, но не для своих типов, объявленных через deftype.


Типы и классы CL воплощают принципиально разные подходы к типизации. Типы CL просто физически невозможно полностью интегрировать с классами. Атомарные какие-то типы - вполне, но полностью - нет. Как ты себе представляешь выражение через классы типов (array тип размерности) или, тем более, (satisfies чтототам)?
Кроме того, более полная интеграция(включая наследование, например) убила бы производительность. Если бы в CLOS была возможность, например, наследоваться от fixnum, то тормозили бы мы как Питон или Ruby какие-нибудь.

Ну и вообще CLOS жирноват для 90% задач.


Возможно, но для 90% задач и VB неплохо подходит. CLOS позволяет решать эти задачи с меньшими усилиями.

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


А вот нет. MOP - очень полезная штука. Конечно, все его фичи сразу использовать нафиг не нужно, но тем не менее, он очень полезен. Для закрепления паттернов проектирования(не только GoF, всмысле, а вообще) в классы, например - очень способствует DRY.

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

продолжение

>Не нравится реализация пакетов (неймспейсов).

Ну вот тут согласен. Пакеты сделаны откровенно хреново, в т.ч. огорчает отсутствие иерархических пакетов. Батя не грит малаца, плохо зделали. В следующей итерации развития лиспа над этой темой надо будет подумать поплотнее.
Также, возможно, неплохо было бы интегрировать пакеты в CLOS. Хотя, тут не всё так однозначно - то же наследование в CLOS концептуально воплощает наследование реализации, а не интерфейса. А интерфейсы сделаны вот как раз через пакеты - по мне, так подобное отделение очень правильно.
Я в своём Doors в плане интеграции с COM даже особенным образом это подчеркнул - то есть, у меня там COM-интерфейсы _приналежат_ классам(т.е. какой-то класс has-a какой-то интефейс), но не являются их предками(т.е. не is-a), как в C++ или .NET, например; и пачка интерфейсов между классами не наследуется, наследуется только их реализация через методы обобщенных функций.

Не нравится отсутствие стандартных средств рефлексии/интроспекции лексического окружения.


Эту тему просто не успели допилить в стандарте, как и MOP. Они к концу очень торопились. Если пишешь конкретную аппликуху под конкретную реализацию, это не должно быть проблемой.

loop - какашка. Это не лисп. Точка.


Loop ок для простеньких циклов. Iterate загромождает код лишними сущностями (например скобками), и некоторыми фичами loop не обладает, и кроме того, там глючный кодволкер(это из-за непортабельного lexenv, естественно, но вообще кодволкеры это зло).
Format тоже ок. В них обоих, конечно, некоторых вещей иногда не хватает, могли бы, конечно, побольше над ними поработать в процессе стандартизации, но вообще, они не так уж плохи, и очень полезны.

unwind-protect - хорошо, но от попыток человеком сэмулировать продолжения для CL хочется икать.


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

Максимум, для чего их можно использовать в real-world(а CL это язык для real-world, а не академическая игрушка) - так это для каких-нибудь пруф-оф-концептных веб-хренотеней. Для остального они просто вредны, как я уже сказал. Они очень сильно усложняют реализацию виртуальных машин и оптимизаторов в компиляторах, они делают интеграцию с неуправляемым кодом(т.е. с сишными либами), как и вообще менеджмент неуправляемыми ресурсами(напр. с файлами) непередаваемым адом, они усложняют рассуждения о ходе выполнения программы(которые в real-world программах и так усложенены той же многопоточностью).

Ну и более мелкие ляпы в стандарте, типа (elt sequence index), но (nth index list).


Это называется «обратная совместимость».

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

>> Я к лиспу вообще слабое отношение имею, но заинтересовался тут Scheme а именно Chicken'ом. Как он в сравнении с CL?

Сюда же : что можно приобрести/потерять при переходе с Racket на CL?

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

> Сюда же : что можно приобрести/потерять при переходе с Racket на CL?

Что значит «переходить»? Это религия такая что ли?

anonymous
()

ЛОР уже не торт, уйду я наверное.

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

>> Сюда же : что можно приобрести/потерять при переходе с Racket на CL?

Что значит «переходить»? Это религия такая что ли?

Бросить изучать Racket и взяться за CL. На оба времени нету.

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

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

> Бросить изучать Racket и взяться за CL. На оба времени нету.

Если тебе для общего развития или just for fun, лучше Схема. Всё остальное зависит от требований конкретного проекта.

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

> Стоит подождать, когда выкатят большой драфт R7RS

Да ну, зачем такие детали. Racket во все поля (будем считать, что это тоже Схема).

anonymous
()

просто функций, присущих только данному классу, нет.

А если функции размещать в слотах? Там же полиморфизм по аккессорам, вроде такого:

(defun make-voice (message)
  (lambda (&optional (stream t))
    (format stream "~A~%" message)))

(defclass dog ()
  ((voice
    :initform  (make-voice "Woof!")
    :initarg   :voice
    :accessor  voice-of
    :type      function)))

(defclass cat ()
  ((voice
    :initform  (make-voice "Meow!")
    :initarg   :voice
    :accessor  voice-of
    :type      function)))

(funcall (voice-of (make-instance 'dog)))
;=> Woof!

(funcall (voice-of (make-instance 'cat)))
;=> Meow!

распознавание класса в CLOS для стандартных лисповских типов ещё работает, но не для своих типов, объявленных через deftype.

А если тип определён как-то так:

(deftype my-type (a b c)
  (if (plusp (+ a b))
      `(array c)
      c))

Ну то есть, произвольный кусок кода генерирует выражение типа (включая and, or и прочие).

На самом деле, классы и структуры это просто структурные типы данных (типы-произведения), а вот с помощью deftype можно делать вариантные (типы-сумы) и зависимые (т.е. смешанные со значениями) типы. Существующие дженерики и методы - ad-hoc полиморфизм времени выполнения (мог бы быть времени компиляции, если бы была система типов, сейчас в SBCL благодаря использованию MOP в compile-time есть некоторые улучшения - например, slot-value для структур превращается в аккессор во время компиляции (в т.ч. для пременных)).

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

Если методы текущей GF выполняют принципиально разные функции - почему бы и нет. Можно даже имена им разные дать. Вообще говоря, &key аргументы у методов не обязаны соответствовать - у одного метода могут быть одни ключевые параметры, у другого - другие. Основные параметры, и &optional, вроде - да, их должно быть в сигнатурах одинаковое количество. Но, надо понимать, что аргументы диспетчеризации в CLOS это не «параметры функции», это аргументы диспетчеризации, то есть «объекты, которые кооперируют».

Вот я жутко не люблю, когда что-то «надо понимать». Оно нелогично и неудобно, но «надо понимать». На самом деле, это или гнилая концепция, или неудачно натянуто на какую-то базовую вещь и унаследовавшее ограничения базовой вещи. Ну вот как можно сделать диспетчеризацию, если у функции есть обязатальные параметры, optional, key, aux, rest? Можно, и вот вам в довесок три страницы заумных правил, как нельзя комбинировать.

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

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

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

А если функции размещать в слотах?

В Gensym сознательно не использовали CLOS. От него в проектах с миллионами строк кода и шестизначной стоимостью было больше проблем, чем выгоды. Чуваки написали свою простенькую объектную систему.

А какой сакральный смысл в объектной системе вообще, если язык замыкания поддерживает? У меня в последнее время на эту тему большие сомнения.

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

Бросить изучать Racket и взяться за CL. На оба времени нету.

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

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

В CL, кстати, только файлы в стандарте есть.

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

Ну как же тут лиспосрач-то получится? Сейчас придет Кука, прочтет псто и напишет «Ну! Я же говорил!». :)

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

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

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

Эта проблема вылезает когда на CLOS пытаются писать как на Java.

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


Message passing - неплохая технология IPC(а в эрланге как раз оно, по сути), но в плане абстрагирования цепочки синхронных вычислений она уступает мультидиспатчу.

Кстати, у меня давно думаются мысли на тему натягивания аналога мультидиспатча на IPC. Попробую вечером эту тему в жж расписать.

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

Эта проблема вылезает когда на CLOS пытаются писать как на Java.

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

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

какой сакральный смысл в объектной системе вообще, если язык замыкания поддерживает?

Ага, а в конце концов всё сводится к обычным функциям.

1) Объектная система, классы и диспетчеризация в методах.

2) Замыкания, функция-объект, сфотографированное окружение в котором лежат «методы» и «слоты», плюс мутабельность.

3) Каррированные функции (:: Object -> Method -> ...), что расщепляется на функцию (Method -> ...) т.е. конкретный объект, и (...) т.е. вызов конкретного метода для конкретного объекта.

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

> А какой сакральный смысл в объектной системе вообще, если язык замыкания поддерживает? У меня в последнее время на эту тему большие сомнения.

скорее замыкания должны быть видны как объекты с выводимым компилятором типом (если замыкания вообще нужны)

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

www_linux_org_ru ★★★★★
()

а теперь по поводу самого поста

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

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

1. выбор академически-ориентированных людей сместился в область type inference

2. понаклепали столько статически типизированных библиотек, что «просто так взять и написать заквоченное s-выражение без всякого тайпчека» нужно редко

3. (личная точка зрения) язык должен базироваться на понятиях «тип это предикат» и «все что может быть сделано во время компиляции, должно быть сделано именно тогда, основываясь на типах»

посему, если хочется в будущем сохранить (и увеличить) свой комфорт, полезно задуматься «а какой type inference *мне* реально нужен? чтобы не объяснять компилятору детский сад, но и чтобы он ловил мои (детские и не только) ошибки»

«а какой type inference *мне* реально нужен?» можно выяснить, посмотрев на свой код

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

к п.2 — понаклепали столько статически типизированных библиотек (причем типизированных довольно разумно — с помощью дженериков)

www_linux_org_ru ★★★★★
()

в 60-х годах, когда машин было мало и программистов было мало, написание статически типизированного кода для конкретной задачи (фортрановские либы не в счет) было нерентабельно

ну и еще не было хороших дженериков и вывода типов, и алгол-68 рассматривали как слишком сложный язык

лисп из той эпохи

www_linux_org_ru ★★★★★
()

Нет человеческих макросов, нет продолжений, нет системы модулей, кривые соглашения об именованиях, loop-оговно и прочий императивный понос. Ах, да, LISP-2 - говно.

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

> и защищенного наследования?

От этого вида наследования отказались в f#. Сознательно.

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

Приобрести - CLOS. Потерять: макросы, продолжения, систему модулей, нормальные соглашения об именованиях, отсутствие loop-оговна и прочего императивного поноса. Ах, да, #`/funcall-о говно и куча лишних форм для введения биндингов - привет! Кстати, кто-нибудь знает, а в CL вообще можно эмулировать схемовский letrec, или для этого надо будет переписать компилятор?

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

> посему, если хочется в будущем сохранить (и увеличить) свой комфорт, полезно задуматься «а какой type inference *мне* реально нужен? чтобы не объяснять компилятору детский сад, но и чтобы он ловил мои (детские и не только) ошибки»

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

язык должен базироваться на понятиях «тип это предикат» и «все что может быть сделано во время компиляции, должно быть сделано именно тогда, основываясь на типах»

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

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

labels биндит функциональный слот. То есть например: (let ((f (lambda (x) x))) (map f `(1 2 3))) - вот так в CL можно а если я попробую сделать f рекурсивной - например факториал какой-нибудь, то все. Только лабел и #'.

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

>А какой сакральный смысл в объектной системе вообще, если язык замыкания поддерживает? У меня в последнее время на эту тему большие сомнения.

Только в последнее время?

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

> А какой сакральный смысл в объектной системе вообще, если язык замыкания поддерживает? У меня в последнее время на эту тему большие сомнения. Собственно ООП - это просто способ смотреть на программу и програмные сущности (трактовать их как объекты, а процесс их взаимодействия - как обмен сообщениями). Как это реализовано - не суть важно уже ведь?

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

(let ((f (lambda (x) x))) (map f `(1 2 3))) - вот так в CL можно а если я попробую сделать f рекурсивной - например факториал какой-нибудь, то все.

Это не вы только что робтали на lisp-2? let/let* и производные биндяд (эх...) в пространстве символов, а flet/labels в пространстве функций, #' осуществляет перевод из первого пространства во второе.

Только лабел и #'.

И?

(letrec ((f (lambda (n)
           (if (zero? n)
               1
               (* n (f (- n 1)))))))
  (f 100))

(labels ((f (n)
           (if (zerop n) 
               1 
               (* n (f (- n 1))))))
    (f 100))

называется найдите 3 отличия ;)

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

> Это не вы только что робтали на lisp-2?

Я.

let/let* и производные биндяд (эх...) в пространстве символов, а flet/labels в пространстве функций, #' осуществляет перевод из первого пространства во второе.

Я в курсе.

называется найдите 3 отличия ;)

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

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

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

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

> А какой сакральный смысл в объектной системе вообще, если язык замыкания поддерживает?

А какой смысл в замыканиях, если поддерживаются объекты? :)

И кстати, как с помощью замыкания сделать объект с несколькими методами? Примерчик, пжалста.

tailgunner ★★★★★
()

Передаю привет mv, tailgunner'у, yogurt'у и другим уважаемым личностям в этом треде. Приятно снова видеть mv на ЛОРе, люблю на выходных такие треды почитать =)

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

Ты не понял.

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

Вот используя let я могу в этом let определить ф-ю и забиндить ее в слот переменной - и в CL и в схемке. И потом я могу эту ф-ю передавать фунаргомбез #'.

С let - всё так (без #'). Но ещё - вот используя labels я могу в этом labels определить ф-ю и забиндить ее [неразборчиво] с символом. И потом я могу эту ф-ю передавать фунаргом _с помощью_ #':

(labels ((recursive (...)
           ...
           ))
  (mapcar #'recursive ...))

А рекурсивную ф-ю в let не определить.

И в схеме тоже. Точнее, только так.

Считай, что labels это letrec с незначительными отличиями, связанными с разделение пространства имён в CL.

То есть никак не получится без #'.

Зачем? Это не Scheme.

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

Примерчик, пжалста.

(defmacro define-class (name &key slots methods)
  `(defun ,name ,slots
     ;; ^ замыкаем переменные (поля), они так и лежат в куче, можно
     ;;   там их изменять.
     (labels ,methods
       ;; ^ замыкаем функции (методы), их менять нельзя (в них можно
       ;;   менять поля), "защищённые" поля можно считать методами без
       ;;   аргументов (но это, конечно, не то).
       (lambda (method)
         (cond ,@(loop :for m :in methods
                       :collect `((eq ',(first m) method) (function ,(first m))))
               (t (error "Undefined method ~A" method)))))))

;; одно "поле", "методы" инкрементации, декрементации.
(define-class num
  :slots
  (n)
  :methods
  ((++ (d) (incf n d) n)
   (-- (d) (decf n d) n)))

(macroexpand-1
 '(define-class num
    :slots
    (n)
    :methods
    ((++ (d) (incf n d) n)
     (-- (d) (decf n d) n))))

;;; =>

;;; (DEFUN NUM (N)
;;;   (LABELS ((++ (D)
;;;              (INCF N D)
;;;              N)
;;;            (-- (D)
;;;              (DECF N D)
;;;              N))
;;;     (LAMBDA (METHOD)
;;;      (COND ((EQ '++ METHOD) #'++) ((EQ '-- METHOD) #'--)
;;;             (T (ERROR "Undefined method ~A" METHOD))))))

(defparameter n (num 10))
;=> N

; n -- объект
;=> #<CLOSURE (LAMBDA (METHOD)) {BB74405}>

(funcall n '++) ; метод
;=> #<CLOSURE (LABELS ++) {BB743E5}>

(funcall (funcall n '++) 5)
;=> 15

(funcall (funcall n '++) 5)
;=> 20

(funcall (funcall n '--) 5)
;=> 15

(funcall (funcall n '--) 5)
;=> 10

(funcall (funcall n '**) 5)
;=> Undefined method **

но всё равно это извращение без каррированных функций (а с ними, это элементы обычного point-free).

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

/me .oO( песец, ну ни слова в простоте )

И чем удобнее обычного класса с четко определенным интерфейсом? Кстати, как это добро наследовать?

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

> геморойней использовать кучу funcall-ов

Ага.

И потом я могу эту ф-ю передавать фунаргом _с помощью_ #'

Ага. Но без #' лучше ведь.

И в схеме тоже.

Зато в letrec в схеме можно. А еще в летреке можно определить рекурсивное замыкание: (letrec ([f (let ([x ...]) (lambda (y) ... x ...))]) ... вызываем f ...) Как такое сделать с labels?

Зачем?

А зачем писать #' и функоллы, если можно не писать?

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