LINUX.ORG.RU

Lisp, Haskell, Smalltalk, Forth... что дальше?


1

0

Навеяно предыдущей темой.

Последнее время совершенно отчетливо прослеживается, как маргинальные языки передают друг другу олимпийский факел, в смысле их популярности на ЛОРе.

Факел был когда-то зажжен незабвенным Проффессором Луговскером, и достался он лиспу. Прошло несколько лет, и теперь даже самый распоследний нубас на ЛОРе расскажет вам про REPL, метапрограммирование, квазиквотацию, eval, Slime и про жаба-monkey-кодеров. Лисп стал популярен на ЛОРе, и утратил позиции «элитного» языка, дискутировать о котором могли единицы.

Ниша долго не пустовала. Некоторое время факел находился у Haskell, а последние месяцы его гордо несет Smalltalk (я сужу исключительно по количеству новостей и дискуссий о нем). Но теперь завзятые маргинальщики начинают интересоваться Forth'ом, из чего я делаю вывод о том, что факел Smalltalk'а начал коптить, в силу его популяризации на ЛОРе.

Предлагаю коллективно поразмыслить над тем, какой язык мог бы принять эстафету у Форта. Из маргинальных неэзотерических языков осталось не так уж и много. Clean? Pure? Factor?

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

>ничего, что специальная форма sizeof используется как функция?

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

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

> Код в котором вызовы функций и макросов выглядят одинаково

не может быть читабельным.


Это вы из практического опыта утверждаете, или просто так размышляете о сферических макросах в вакууме?

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

А в каком языке макросы нужно вызывать как-то по особому?

Просто в CL половина вещей - макросы (cond, case, defun, defmacros, defpackage, да на самом деле - всё что не функций или примитив - макрос :) т.е. всякие управляющие формы) если бы с ними пришлось как-то церемониться - ну, не знаю.

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

>Это вы из практического опыта утверждаете, или просто так размышляете о сферических макросах в вакууме?

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

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

> для реализации базовых специальных форм (case) вы не можете себе позволить тянуть CLOS.

да, просто основная идея была в том, что не стоит всё пихать в одну функцию — при необходимости добавления нового варианта придётся переписывать её саму. конечно в данном случае вместо методов достаточно было простой хеш-таблицы. =)

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

> Хоть это и была практика разбора кода на C, но принцип тот же. Разобраться в таком коде сложно, надо каждый раз выяснять функция это или макрос. Единственное что на C этим не так злоупотребляют как в лиспах.

зачем каждый раз? один раз выяснил и запомнил.

Вы когда неизвестную функцию находите, не читаете в документации что она делает?

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

>А в каком языке макросы нужно вызывать как-то по особому?

В коде на Template Haskell сразу видно где подставляется шаблон и это упрощает отладку чужого кода.

Кроме того во многих скриптовых языках можно эмитировать препроцессор посредством eval. Преймущество то же самое, сразу видно что это eval.

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

>зачем каждый раз? один раз выяснил и запомнил.

иногда их слишком много.

Вы когда неизвестную функцию находите, не читаете в документации что она делает?

документация не всегда существует.

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

> иногда их слишком много.

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

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

конечно в данном случае вместо методов достаточно было простой хеш-таблицы. =)

Так как предикаты для типов и соответствующие предикаты сравнения все заранее известны - достаточно одного alist ;)

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

Или, если не жалко функций, по такой схеме:

(macrolet ((def-frob! (foo)
              ...
              `(defun ,foo ... )))
  (def-frob! one)
  (def-frob! two)
  (def-frob! three)
  ...)
quasimoto ★★★★
()
Ответ на: комментарий от SV0L0CH

> иногда их слишком много.

документация не всегда существует.


В SLIME есть чудесная команда - M-., которая открывает определение формы: доля секунды и ты видишь код неизвестной функции/макроса. Поскольку нормальный код на CL обычно хорошо понимается (ибо краток и выразителен), то разбираться с незнакомым кодом одно удовольствие. А эти ваши рассуждения сплошная демагогия и домыслы, не соответствующие реальной практике. Большое количество библиотек для CL идёт без документации и это не создаёт каких-либо проблем, ибо читать код проще и быстрее. Я, например, даже не удосужился полностью прочитать документацию на Hunchentoot, ибо это просто не нужно. И вместо того, что бы смотреть Hyperspec, часто просто смотрю исходники SBCL: emacs-w3m запускается дольше, чем открывается нужный код.

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

> Так как предикаты для типов и соответствующие предикаты сравнения все заранее известны - достаточно одного alist ;)

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

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

>SLIME

Ладно, попробую поковырять это IDE. А всё остальное написанное подозрительно напоминает мне мою практику работы с JS.

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

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

Например? Определи такой тип T, чтобы переменная V :: T не попала в typecase для заданного набора примитивных типов (полный фиксированный набор примитивных типов в данной реализации). Дело ведь в том, что сложные типы конструируются как теоретико-множественные конструкции от примитивных типов. Например, реализация equal - этой набор cond-ов для фиксированного набора примитивных типов, но она может работать и для всех сложных типов, так как те сводятся к примитивным.

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

> лисп-лайк символы тебе не помогут, если get_x получает свой char* откуда-то извне, например из (mmap-нутого) файла

Ну если смотреть в таком разрезе, то был не совсем прав.

Правда тогда этот частный swith по строкам плавно превращается в readtable.

Но как это пример относиться к

макросы нужны немного в иной форме — в основном на «чтение AST», создание же AST делать, вероятно, другим методом

я все равно не пониманию. Пока это все описание все тех же макросов с большой буквы L. Но может детали в статье прояснят картину. Ждем вобщем.

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

get-equaler-p работает как спецификатор только для (and atom (not symbol))

Но это хрен целых фиг десятых процентов всех случаев Вы часто используете формы следующего вида?

(my-case 12
  (day "this is day")
  (month "this is month")
  (year "this is year"))

Бред полный

Но это как бы очевидно - как его для символов заставить работать в рантайме

Вот это и не нравится, что либо надо заниматься «рантайм-диспатчингом», либо лепить кейворды как в loop-е

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

Но это хрен целых фиг десятых процентов всех случаев Вы часто используете формы следующего вида?

что либо надо заниматься «рантайм-диспатчингом», либо лепить кейворды как в loop-е

А тебя трудно парсить :) Но не стоит так волноваться - что касается того, как реализовать case (чтобы и для строк работал), то тут такие варианты:

1) Элементарнейший макрос через equal:

(defmacro my-case (object &rest cases)  
 `(cond ,@(mapcar #'(lambda (case)  
            `((equal ,(first case) ,object) ,@(rest case)))  
        cases)))

Стандартный case отличается тем, что там (при апелляции к case-like раскрывателю в данной реализации) стоит :test #'eql

2) Мой второй вариант в котором для константных выражений выбирается наилучший предикат, а для переменных - по прежднему equal.

Это не нужно! Так как такие вещи нужно обеспечивать с помощью source-transformation, а не с помощью макросов (source-transformation это такая фишка из по-настоящему-хороших-реализаций-CL).

3) Вариант Korvin_ с оценкой в рантайме.

Это тоже не нужно! Такой оценкой мы просто эмулируем equal, т.к. тот как раз и занимается тем что в рантайме (функция) узнает тип и сразу вызывает наилучший предикат. Причём при велосипедостроительстве мы не можем быть уверенны в правильности наших действий, а при использовании equal - можем (например, что там предусмотрены _все_ типы).

Короче для развлечения - нужно писать в стиле (1) и не париться.

А если серьёзно, то:

(in-package "SB-IMPL")

(defmacro-mundanely Ъ-case (keyform &body cases)
  (case-body 'case keyform cases t 'equal nil nil nil))

(export 'Ъ-case)
quasimoto ★★★★
()
Ответ на: комментарий от SV0L0CH

В коде на Template Haskell сразу видно где подставляется шаблон и это упрощает отладку чужого кода.

Там такой дизайн - TH появился позже самого языка (в котором просто нет макросов) как расширение.

А у CL другой дизай, в котором отличие макросов и функций довольно тонкое. Как бы не имеет смысла в том, хороший это дизайн или плохой - он хороший :) Проблемы могут быть в другом.

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

например для использования более эффективных или более нужных пользователю функций сравнения

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

> 2) Мой второй вариант в котором для константных выражений выбирается наилучший предикат, а для переменных - по прежднему equal.

он у тебя выбирается в рантайме, потому что ты генериш код выбора предиката по типу object, а нужно в функции раскрытия my-case выбирать предикат по константам кейсов, ибо их типы известны в compile-time

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

например для использования более эффективных или более нужных пользователю функций сравнения

Тогда ладно. Лишь бы пользователь чего не напутал. Почему-то equal это такой чёрный ящик и не позволяет вводит более «эффективных» (правда? но он нам не верит!) функций :)

он у тебя выбирается в рантайме, потому что ты генериш код выбора предиката по типу object, а нужно в функции раскрытия my-case выбирать предикат по константам кейсов, ибо их типы известны в compile-time

Для атомов-не-символов выбор происходит при раскрытии макроса (compile-time). Т.к. функция возвращающая предикат вызывается во время раскрытия, ну и предикат встаёт на своё место. Это проверяется, т.е. всё так - «а нужно в функции раскрытия my-case выбирать предикат по константам кейсов, ибо их типы известны в compile-time», я не понял возражения.

Для символов-переменных - equal работает в runtime, и это лучше, чем в runtime-же пытаться эмулировать equal с помощью typecase.

Правильно?

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

> я не понял возражения.

у тебя тип берётся от object из "(my-case object ...)"

понятно, что когда там константа, тип можно определить во время компиляции, но можно тип определять по value "(my-case object (value-1 ...) (value-2 ...) ...)" ибо они всегда константы и соответственно их тип известен во время компиляции и следовательно даже для случаев, когда в качестве object передаётся не константа, а переменная.

Почему-то equal это такой чёрный ящик и не позволяет вводит более «эффективных» (правда? но он нам не верит!) функций

но это не значит, что пользователь не может определить свою функцию/метод сравнения и использовать его везде вместо equal — большиство функций предоставляют параметр :test, а макры типа case можно написать свои...

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

А, понял - не знал, что у case не может быть переменной в clause.

определить свою функцию/метод сравнения и использовать его везде вместо equal

А можно пример каких-нибудь сложных типов когда это может иметь смысл? Я всегда встречал такую практику - когда известно что типы могут быть сложными объектами, то просто выбирают наиболее мощный предикат (eql или equal). И как раз для всех функций используют :test #'equal.

Например из спецификации:

;; предикат
(defun square-matrix-p (a)
  (or (< (array-rank a) 2)
      (apply #'= (array-dimensions a))))

;; тип
(deftype square-matrix (&optional type size)
  `(and (array ,type (,size ,size))
        (satisfies square-matrix-p)))

;; но для сравнения использовать мы должны equal

макры типа case можно написать свои...

Всё-таки я за простой equal и за improve существующих реализаций...

В SBCL определить свой case для своего предиката сравнения (если уж это так нужно) - это одна строчка.

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

вероятно, в т.ч. и под влиянием критики на ЛОРе лисперы перешли к более интересной точке зрения:

Не вероятно, не под влиянием критики (тем более, на ЛОРе*), и не перешли.

* Кто критикует-то? Прочитавшие пару глав какой-нибудь книжки, да поискавшие, как выйти из Емакса?

mv ★★★★★
()

Надо учить аду.

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