LINUX.ORG.RU
ФорумTalks

Более лучший Лисп

 


3

9

Что бы вы изменили и каким образом в своем любимом/нелюбимом диалекте Лиспа, чтобы он стал ещё лучше? Расскажите обо всех своих грязных фантазиях.

Лиспосрач (и те два унылых анонимуса) не приветствуется.

Перемещено tazhate из development

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

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

(defstruct foo ...)

(defgeneric meth (object))

(defmethod meth ((foo foo)) ...)

(declaim (ftype (function (foo) null) test))
(defun test (object) (meth object) nil)

Почему тут нельзя вызывать не диспетчер meth, а специализацию вроде meth@foo (допустим, safety = 0 и т.п.)?

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

Соответственно, в методах нельзя провести диспетчеризацию по «вектор длиной 3», «массив именно fixnumов»

В Scala по этому поводу пишут «тип mutable контейнера должен быть инвариантен, immutable — ковариантен». В смысле, от изменения значения в массиве или его длины, его класс меняться не должен. Тем более, неясно в каком порядки применять специализаторы «вектор целых» и «вектор длины 3».

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

Почему тут нельзя вызывать не диспетчер meth

Потому, что после (remove-method #'meth (find-method #'meth '() (list (find-class 'foo)))) функция test должна не ломаться, а продолжать работать вызывая следующую специализацию. Если же дженерик не должен менять специализацию, то он должен быть не дженериком, а функцией.

P.S. Балдею от новой моды

(defgeneric myclass-p (obj)
  (:method ((obj myclass)) t)
  (:method (any) nil))

вместо

(defun myclass-p (obj)
   (typep obj 'myclass))

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

Но вообще это на тему того что в гуглостилях предлагают статическую типизацию x : T реализовывать разбрасыванием разных x по разным пакетам

Это просто лисповая реализация возможности сделать vector.length() и box.length(units). Собственно пакет — это не тип, а «протокол» (например, типа alist или plist вообще нет, а протокол есть).

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

cons -> list это не совсем то, так как list это не только пары, но и nil, нужен null = { nil } и сумма list = null + cons, в виде (defclass null () ()) и (defclass list (null cons) ()), например. А так это то же самое что defstruct + deftype + defstruct-wrapper только за счёт механизма наследования defclass, тоже боксирование в итоге.

А можно как-то составить вложенную структуру используя только make-instance 'cons и потом сказать «это у нас list» и вызывать copy который сработает как copy-list потом этой же структуре сказать «это у нас tree» и вызвать соответствующий copy?

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

В смысле, от изменения значения в массиве или его длины, его класс меняться не должен.

Если я хочу параметризировать тип значениями чтобы выразить какие-то более тонкие инварианты, то как раз должен. Например, vector<char, 4> и vector<char, 8> в плюсах это просто разные типы для массивов разного рода (разной длины), по ним можно специализировать шаблоны.

Тем более, неясно в каком порядки применять специализаторы «вектор целых» и «вектор длины 3».

С помощью :before, :after, :around и т.п. (method combinations).

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

вызывая следующую специализацию

ох ши... Ну тогда да - всё правильно.

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

А так это то же самое что defstruct + deftype + defstruct-wrapper только за счёт механизма наследования defclass, тоже боксирование в итоге.

Классы без боксирования в lisp не бывают. На тэгах много не сделаешь — там всего несколько бит.

А можно как-то составить вложенную структуру используя только make-instance 'cons и потом сказать «это у нас list» и вызывать copy который сработает как copy-list потом этой же структуре сказать «это у нас tree» и вызвать соответствующий copy?

Или change-class или make-instance с копированием типа

(defun as-list (obj) (make-instance list :car (car obj) :cdr (cdr obj)))

Собственно, всё как в Java/C++.

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

в каком порядки применять специализаторы «вектор целых» и «вектор длины 3».

С помощью :before, :after, :around

Я имею в виду

(defmethod test ((arr (array t 3))) 1)
(defmethod test ((arr (array integer))) 2)

(test #A(1 2 3)) ?
monk ★★★★★
()
Ответ на: комментарий от quasimoto

vector<char, 4> и vector<char, 8>

Это типы, создаваемые по месту.

Так и в CL можно

(defmacro vector-t (elem-type size)
  (let (name (symbolicate 'vector- elem-type '- size))
     `(or (find-class ,name)
          (defclass ,name (vector) ())
          (defmethod initialize-instance ((,name ,name) &rest args)
              (make-array ,size :element-type ,elem-type :adjustable nil)))))

(defmethod test ((arr-int4 #.(vector-t integer 4)))
  ...)
monk ★★★★★
()
Последнее исправление: monk (всего исправлений: 1)
Ответ на: комментарий от monk

1. В порядке определения.

2. В явно заданном порядке типа

(defmethod test :order 1 ((arr (array t 3))) 1)
(defmethod test :order 2 ((arr (array integer))) 2)

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

(defmethod test ((arr (array t 1))) 1)
(defmethod test ((arr (array t 2))) 2)
;; и / или
(defmethod test ((arr (array t size))) size)
quasimoto ★★★★
()
Ответ на: комментарий от monk

Собственно пакет — это не тип, а «протокол» (например, типа alist или plist вообще нет, а протокол есть).

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

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

В порядке определения.

При инкрементальной компиляции будут фееричные результаты

На самом деле нужны вещи вроде

(defmethod test ((arr array))
  (test-array arr (length arr)))

(defmethod test-array ((arr array) (length (eql 1))) 1)
(defmethod test-array ((arr array) (length (eql 2))) 2)
(defmethod test-array ((arr array) length)) length)
monk ★★★★★
()
Последнее исправление: monk (всего исправлений: 1)
Ответ на: комментарий от quasimoto

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

Посмотри, что такое конструктор копирования в C++

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

Это типы, создаваемые по месту.

Так и в CL можно

В данном случае С++ хорошему не научит :) Лучшим примером будут фантомные типы.

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

Посмотри, что такое конструктор копирования в C++

Зачем? Делая static_cast для потомков к родителям и reinterpret_cast вообще можно синтаксически менять тип, так что и каст и выбор нужного метода будут происходить во время компиляции.

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

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

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

И какая ниша дозволена для лиспа?

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

Helloworld-ы и факториалы?

Общелисп как язык абсолютно спокойно перекрывает возможности распространенных динамически-типизированных языков: ruby, python, php, perl, smalltalk, tcl.

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

Делая static_cast для потомков к родителям и reinterpret_cast вообще

Тоже можно. Как-то так:

(defclass a () ())
(defclass b (a) ())
(defmethod test ((a a)) 1)
(defmethod test ((b b)) 2)
(test (make-instance 'b)) => 2

;; А в следующей строчке cast

(funcall (closer-mop:method-function 
           (find-method #'test () (list (find-class 'a)))) 
         (list *b*) nil) => 1

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

Общелисп как язык абсолютно спокойно перекрывает возможности распространенных динамически-типизированных языков: ruby, python, php, perl, smalltalk, tcl.

Если не считать библиотек. А если их не учитывать, то лучшим можно nemerle, или даже http://www.chrisseaton.com/katahdin/ посчитать

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

Общелисп как язык

Если не считать библиотек

Как язык.

А если их не учитывать, то лучшим можно nemerle, или даже katahdin

Nemerle не динамически типизированный. Katahdin выглядит игрушкой и где там реализация семантики haskell, например?

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

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

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

В Nemerle неполноценные макросы (немногим лучше чем в TH), а в Katahdin все для синтаксиса, но нет ничего для расширения семантики.

Лучше бы тогда MPS назвал.

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

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

Не боись, руководство в курсе.

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

Ты уныл. Придумал бы уже что-то новое

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

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

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

так что, объясни развёрнуто свою позицию.

Частично выше уже ответили:

1. Все остальные лиспы не так распространены, как CL (ну ладно, схема под вопросом, но там свои тараканы). Почему - трудно сказать, думаю, что и стандарт сыграл некоторую роль, ибо, фактически, на момент принятия он имел какую-то преемственность «предков» + в нём содержится несколько «библиотечных модулей». Посему следующая версия ISLISP-а не интересна совершенно (ибо и делать стандарт ни кто не будет, и тем более реализацию оного), а если вдруг чудом появиться, да ещё и со свободной реализацией, не уступающей по своим характеристикам sbcl - то... Да ну, скорее я увижу живого динозавра.

2. Следующий стандарт CL? Да тут в ветке УЖЕ договориться не могут, а обсуждается то пока сущая ерунда. А без поддержки большей части сообщества - кто будет реализовывать новый стандарт? Ну и РЕСУРСЫ: имхо, сейчас сообщества хватает только на то, чтобы хоть как-то поддерживать основные реализации и библиотеки «на плаву». Хотя вот c-lisp почти заморожен, sbcl чёрте-сколько реализовывал многопоточность под оффтопиком, и то дело ещё не закончено (по мелочи). Где взяться людским ресурсам для формирования нового стандарта и его реализации? Я подобного «источника» не вижу.

Вот как-то так.

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

Тоже можно

Не пойму в чём это статические касты, то есть чем отличается от change-class.

Вот пример со статическим кастом:

#include <cassert>
#include <cstdio>

template <typename T>
struct numeric_list {

    T el;
    numeric_list<T> *next;

    T sum()
    {
        T res = 0;

        for (numeric_list<T> *it = this; it; it = it->next)
            res += it->el;

        return res;
    }

};

template <size_t max_size, typename T>
class numeric_list_arena {

    numeric_list<T> arena[max_size];
    size_t size;

  public:

    numeric_list_arena() : size(0) {}

    void add(T el_)
    {
        assert(size < max_size);

        arena[size].el = el_;
        arena[size].next = 0;

        if (size > 0)
            arena[size - 1].next = &arena[size];

        ++size;
    }

    void to_ring()
    {
        arena[size - 1].next = arena;
        size = 0;
    }

    numeric_list<T>* get_numeric_list()
    {
        return arena;
    }

};

template <typename T>
struct numeric_ring : public numeric_list<T> {

    T sum()
    {
        T res = 0;

        numeric_list<T> *it = this;
        do {
            res += it->el;
            it = it->next;
        } while (it && it != this);

        return res;
    }

};

#define like(type, object) static_cast<type&>(object)

int main()
{
    numeric_list_arena<10, int> arena;

    for (int i = 1; i <= 10; ++i)
        arena.add(i);

    numeric_list<int> &lst = *arena.get_numeric_list();

    printf("%d\n", lst.sum());

    arena.to_ring();

    printf("%d\n", like(numeric_ring<int>, lst).sum());
}

/*
	pushq	%rbx
	xorl	%eax, %eax
	subq	$176, %rsp
	movq	%rsp, %rbx
	movq	%rsp, %rdx
	jmp	.L2
.L12:
	movq	%rax, %rcx
	addq	$1, %rax
	salq	$4, %rcx
	addq	%rbx, %rcx
	cmpq	$10, %rax
	movq	%rcx, -8(%rdx)
	je	.L11
.L4:
	addq	$16, %rdx
.L2:
	leal	1(%rax), %ecx
	testq	%rax, %rax
	movq	$0, 8(%rdx)
	movl	%ecx, (%rdx)
	jne	.L12
	movl	$1, %eax
	jmp	.L4
.L11:
	movq	$10, 160(%rsp)
	xorl	%esi, %esi
	movq	%rsp, %rax
.L5:
	addl	(%rax), %esi
	movq	8(%rax), %rax
	testq	%rax, %rax
	jne	.L5
	movl	$.LC0, %edi
	call	printf
	movq	160(%rsp), %rax
	xorl	%esi, %esi
	movq	$0, 160(%rsp)
	subq	$1, %rax
	salq	$4, %rax
	movq	%rbx, 8(%rsp,%rax)
	movq	%rsp, %rax
.L7:
	addl	(%rax), %esi
	movq	8(%rax), %rax
	testq	%rax, %rax
	je	.L6
	cmpq	%rbx, %rax
	jne	.L7
.L6:
	movl	$.LC0, %edi
	xorl	%eax, %eax
	call	printf
	addq	$176, %rsp
	xorl	%eax, %eax
	popq	%rbx
	ret
*/

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

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

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

Где-то я уже это слышал. ;-)

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

кстати, den73 — а почему используешь общелисп, а не схему, например?

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

То есть, к тебе два вопроса:

1. Почему и где используешь лисп. Какие фичи лиспа нашли применение, к какой задаче. Из того, что я понял — это управление жизненным циклом приложения в разрезе миграции скриптов БД. Отсюда естественным образом вытекает REPL, инкрементальная разработка через образ, возможно, рефлексия и MOP/CLOS. Можно это переформулировать как-то оторванно от общелиспа?

2. Почему используешь именно общелисп, рассматривались ли другие альтернативы. Схема, например, как наиболее распространённый лисп без детских болезней.

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

затем, что mynicefunction нифига не читабельно.

это ж символьные вычисления. Храни в символе mynicefunction свойство с MyNiceFunction, напиши обёртки — свой print/pretty printer, обновление символа с учётом этого свойства.

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

Вот её бы в стандарт внесли

Сначала нужно разрешить дихотомию встроенных типов с deftype и CLOS типов, тогда можно сделать generic first для класса list и вместо (list:first object) писать просто (first object) - для динамических объектов будет обычная динамическая диспетчеризация CLOS, а для символов с известным синтаксическим типом - статическая, то есть просто вызов функции.

либо, сделать всё first class objects, как в Dylan / goo / ISLisp ILOS : интегрировать все типы в объектную систему.

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

Ну, и шоб два раза не вставать — почему вместо Delphi не рассматриваешь какой-то Blackbox Component Pascal с «коммандерами» вместо REPL-а ? Как жалкую замену лиспа паскалем?

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

Да тут в ветке УЖЕ договориться не могут, а обсуждается то пока сущая ерунда. А без поддержки большей части сообщества - кто будет реализовывать новый стандарт?

Это и есть основная проблема. Скажем в проекте hu.dwim сделана внутренне-целостная система (логгер, юнит-тесты, интеграция с asdf, интеграция с slime, слой взаимодействия с БД, ORM, web-сервер, зависимые (вычислимые) слоты, улучшенный синтаксис, ...). Фактически, на его основании можно писать новый стандарт.

Но, некоторые люди (например, Archimag) считают внесенные в hu.dwim изменения синтаксиса настолько ужасными, что не используют ни одну из библиотек hu.dwim.*

Чтобы было понятно, о чём речь, пример кода:

(def print-object (redirect-response :identity #f :type #t)
  (princ (target-uri-of -self-)))

(def constructor redirect-response
  (setf (header-value -self- +header/status+) +http-moved-temporarily+)
  (setf (header-value -self- +header/content-type+) +utf-8-html-content-type+)
  (setf (external-format-of -self-) (ensure-external-format :utf-8)))

Другая крайность — den73 с camelCase, синтаксисом вида object^field^method, основной конструкцией proga, которая делает область видимости для let от определения до конца конструкции (как int i = 1 в С). Тоже в целом можно считать всё целостным языком, но, опять же, на любителя.

А стандартизовать «что-то» смысла нет. Вон ISLISP от 2007 года. Даже свободный компилятор есть (OpenLisp). И что? Кому-то нужен?

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

ага, Lisp Flavoured Erlang вместо общелиспа, лол.

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

1) Динамическая диспетчеризация в методах по типам это больше боксирования. Пока язык в основе своей динамический это может быть слишком дорого.

не очень. Гуглить в сторону predicate dispatching , C3 superclass linearization (похожая линеаризация суперклассов, ЕМНИП, есть в Scala)

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

как в Dylan / goo / ISLisp ILOS : интегрировать все типы в объектную систему.

Там можно явно указать тип встроенному объекту? Типа '((1 2) 3) as tree. Или просто class-of даёт осмысленные классы для встроенных объектов? Если последнее, так оно и в CLOS есть. Но не даёт разделять copy-list от copy-tree.

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

любопытно, как на этом будут выглядеть Go-подобные ad hoc интерфейсы?

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

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

man predicate dispatch. Частный случай, если вынести все вычисления предикатов во время CTFE, то — ПРОФИТ.

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

в частности, VTable/ VPTR в C++ — это частный случай прекалькулированной таблицы с учётом предиката is-a на type inclusion тесты.

PDF в тему, обзор эффективных алгоритмов райнтайм-окружения ООП языков.

в некотором роде, «накладные расходы на ООП»

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

в частности, VTable/ VPTR в C++ — это частный случай прекалькулированной таблицы с учётом предиката is-a на type inclusion тесты

в частности, VTable/ VPTR в C++ — это частный случай прекалькулированной таблицы с учётом предиката is-a на type inclusion тесты и single dispatching-а

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

Схема
без детских болезней

Жги ещё.

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

Но, некоторые люди (например, Archimag) считают внесенные в hu.dwim изменения синтаксиса настолько ужасными, что не используют ни одну из библиотек hu.dwim.*

Можно ли использовать эту библиотеку без использования её синтаксиса? (сорри - лень лезть и читать) Или использовать частично? Или использовать не «инклудя» её синтаксис?

А так - да, многие «олдфаги» не приемлют «необоснованного» (попробуй обосновать! :)) изменения синтаксиса, которое необходимо тащить в рабочий «нэймспейс».

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

1. Все остальные лиспы не так распространены, как CL (ну ладно, схема под вопросом, но там свои тараканы). Почему - трудно сказать, думаю, что и стандарт сыграл некоторую роль, ибо, фактически, на момент принятия он имел какую-то преемственность «предков» + в нём содержится несколько «библиотечных модулей».

я так понимаю, что CL в момент становления, 1985..до времени выпуска CLtL и стандартизации ANSI — это по сути был кодифицированной смесью сложившихся в Maclisp / Interlisp идиом. то есть, стандарт на исторически сложившуюся, полезную в практике семантику и особенности применения. что-то вроде книжки с анектодами или «tao of unix programming», но почему-то в виде кода.

отсюда такая громоздкость и труднообозримость стандарта.

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

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

вообще, если мы посмотрим в википедии timeline (нажми там show preview), то увидим какие-то странные вещи: практически в одно время, рядом друг с другом тусуются схема, общелисп, изялисп — причом схема даже древнее лет на десять, а на слуху только общелисп. общелисп и изялисп появляются примерно в одно время, но не смешиваются, как вода и масло. хотя, по уму, историю общелиспа надо считать от маклиспа с интерлиспом как историю книжки с анектодами, тогда да, у общелиспа огромная борода выросла.

(хотя, что это за общелисп с flavors вместо clos-а, например? в какой момент общелисп можно считать панимаишь, преемникоми  — где заканчивается Interlisp / ZetaLisp и начниается общелисп?)

+ в нём содержится несколько «библиотечных модулей».

дак в той же схеме модулей и поболее будет.

притом, в разных реализациях много чего будет «из коробки», что немного снимает потребность в каких-то ещё модулях.

другое дело, что это должны быть полноценные пакеты типа CTAN, например пакеты в chicken схеме и т.п.

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

cтандарт есть, последний раз обновлялся недавно. чего не хватает — так это вменяемых свободных реализаций. есть с открытыми исходниками (а то тут могут быть свободные, но без сорцов) — например, есть на яве, и 2-3 на сишечке (с няшными иероглифами в исходниках).

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

у изялиспа — простой встраиваемый в сишечку (например, в кирпичеКАД) лисп, с простой интеграцией. это как-то скрашивает вопрос с библиотеками.

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