LINUX.ORG.RU
ФорумTalks

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

 


3

9

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

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

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

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

При опыте работы >3 лет она соответствует действительности

Я ищу человека на удалёнку, а не в конкретном городе.

DSL это ...

не хочу это обсуждать.

можешь все-таки ответить на вопрос «почему лисп?»

во-первых, исторически сложилось, во-вторых, при всех недостатках он мне всё равно нравится.

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

Оба проекта - это NoSQL + NoC++.

Можно сказать и так. И вспомнить OpenMW.

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

Почему и где используешь лисп

Где:

- поддержка ЖЦ базы данных (хранение сорсов, сборка процедур и вьюх, раздача прав, макросы для SQL, генерация некоторых триггеров, процедур удаления дубликатов). Всё это можно было бы сделать и на самом SQL, но на смеси SQL и лиспа - намного удобнее.

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

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

1. можно падать в отладчик в контексте приложения и видеть контекст

2. можно свободно писать в логи, в файлы, на консоль, иметь несколько транзакций и т.п.

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

4. горячая замена более горячая, чем на сервере SQL (Firebird блокирует испольуземые объекты).

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

рассматривались ли другие альтернативы

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

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

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

Ок, уточню. Нужен стандартный кроссплатформенный гуй с возможностью сделать на нём что-то с НУЛЕВЫМ фактором неожиданности. Вид хуже чем у Tk пять лет назад - это очень даже не нулевой фактор неожиданности.

Кроме того, вы же вроде давали ссылочку на отрисовщик McCLIM через GTK, нет?

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

Нужен стандартный кроссплатформенный гуй с возможностью сделать на нём что-то с НУЛЕВЫМ фактором неожиданности.

О! Ну тогда или мой https://github.com/Kalimehtar/gtk-cffi или http://common-lisp.net/project/cl-gtk2/ или http://common-lisp.net/project/commonqt/

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

Кроме того, вы же вроде давали ссылочку на отрисовщик McCLIM через GTK

CLIM сам по себе достаточно неожиданен. Примерно, как Emacs. Идея «каждый виджет имеет REPL» всё-таки необычна. А так там выбираешь gtkairo из стандартных отрисовщиков или cl-gtk2-cairo и вид более-менее ничего (что не отменяет бедности интерфейса в плане количества настроек и событий у виджетов)

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

не очень

В том контексте «тип» это примитивный тип вроде array или синоним типа deftype в CL. По примитивным типам позволять диспетчеризацию это нормально, но никто не делает диспетчеризацию по синонимам типов - ни C++ по typedef, ни Haskell и Scala по type, в них диспетчеризация по синониму это диспетчеризация по тому для чего синоним - синоним, если сделать два разных синонима указывающих на один и тот же тип, то при диспетчеризации будет конфликт.

В CL проблемы в том, что

1. Есть параметрически полиморфные примитивные типы вроде array и по ним нельзя проводить диспетчеризацию в методах CLOS.

2. По синонимам типов deftype нельзя проводить диспетчеризацию. Если через #. => 1.

3. Нельзя сделать производный параметрический классоид (структура в базовом CL без CLOS), только с помощью deftype => 2.

4. Нельзя сделать производный параметрический класс, только с помощью deftype => 2.

Соответственно, нет ни параметрически полиморфных контейнеров кроме примитивных, ни фантомных типов (хотя без статики они и не нужны особо, да).

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

github у меня глючит. В саппорт писать лень. :P

https://bitbucket.org/naryl/ql-libs-analizing/src

* (setf *recursive* t)

* (system-apropos «xml» 3)

(3 «cxml-test») 
(4 «s-xml-rpc») 
(5 «cxml-dom») 
(5 «bknr.xml») 
(5 «cxml-klacks») 
(6 «cxml-xml») 
(6 «hu.dwim.quasi-quote.xml») 
(7 «cl-libxml2») 
(8 «cxml-stp») 
(9 «xmls») 
(16 «s-xml») 
(59 «cxml»)

Можешь отправить от своего имени LinkFly.

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

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

Так они и разбегаются, как только глубже в него заглянут, как и в случае с CL. Я тоже когда-то писал на PHP, теперь аналогичные задачу решать на PHP не стал бы по своей воле. Раньше PHP был в моде и новички в программировании нередко начинали с него, за последние несколько лет, я не слышал упоминаний о нем от новичков, все хотят Python и Ruby.

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

Тут соглашусь, только дополнил бы — «большинство посредственных программистов».

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

По вашей логике получается, что писать все нужно на жабе. Привыкнуть не сложно, а плюсов масса. Не понимаю в чем может быть фан от попыток натянуть свою задачу на какой-нибудь убогий язык, а «нужное и полезное» это все достаточно субъективно — разве нужно и полезно кому нибудь писать ещё один веб-фреймворк? Уверен, что для большинства это выглядит как пустая трата времени.

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

Если делать change к предку, можно потерять данные полей, который только у наследника

В данном случае - делаем downcast от конкретного класса (cons/list) к «протокольным потомкам» (alist/tree/...) не добавляющим данных, только интерфейсные методы. Так что можно сделать with-change-class который будет опускать класс, что-то вызывать и потом поднимать его обратно - будет динамический аналог скального with.

полный аналог

Если с #.(closer-mop:method-function ...) - возможно.

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

with-change-class

Можно, но долго (причём в runtime).

Если с #.(closer-mop:method-function ...)

Ну тут уже плюс-минус динамика. Если #., то завязываемся на то, что этот метод есть и никуда не денется. Хотя для рабочих систем это на 99% верно. Вообще, много ограничений лиспа проистекают из того факта, что любой от может внезапно исчезнуть или стать совсем другим. Единственное ограничение налагается на defstruct.

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

почему вместо Delphi не рассматриваешь какой-то Blackbox Component >Pascal с «коммандерами» вместо REPL-а ?

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

А что такое коммандеры? Можно в двух словах?

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

3. Middle-tier: сервер приложений с бизнес логикой а-ля 1С.

у меня есть парсер языка 1С, который выдаёт S-выражения, макросами разворачивается в обычный лисп. то есть, бизнес-логику получаем работающую. которую потом макросами переделаем в какую-то слоистую архитектуру, например, AspectL.

вожусь с ним понемногу, just for fun. в качестве СУБД хочу прикрутить какой-то NoSQL, сейчас играюсь с Cache.

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

дай знать когда что-то получится из прототипа.

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

да и вообще, наличие типа (or a b) ломает всю красивую картину ООП и делает диспетчеризацию по произвольным типам принципиально невозможной. Но это проблема не системы типов, а просто вот так устроена природа. Или, можно ещё высказать мнение, что CLOS плохо прирастили.

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

у меня есть парсер языка 1С, который выдаёт S-выражения, макросами >разворачивается в обычный лисп.

Очень интересно. Можно сюда залить пример на 10 строк кода на 1С, во что он превращается после трансляции и во что превращается после макрорасширений? Синтаксис языка 1С считаю годным.

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

А что такое коммандеры? Можно в двух словах?

это REPL для паскаля — cм. про «Выполнение программы с помощью коммандера». или вот.

вообще, в двух словах про BlackBox Component Pascal :

следующая версия языка Oberon-2, компонентый паскаль. интро.

язык из семейства паскалей, с поддержкой компонентного программирования (см. книгу Клеменса Шиперски про то, что он понимает под этим термином).

компонентное программирование := объекты + модули + динамическая перезагрузка модулей (отсюда вытекает требование к сборщику мусора) + программирование на основе интерфейсов между модулями (компонентами).

BlackBox реализован как система:

1. запускается приложение BlackBox. Это — система, которая включает в себя IDE с документно-ориентированным интерфейсом. cистема может быть без приложений, или с приложениями на основе BlackBox (см. как создать .exe)

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

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

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

тут как-то неаккуратненько, потому что BlackBox — вещь в себе, то есть вместо make нужно компилировать/выгружать/загружать из самой среды. есть компонент, который это всё делает (топологическая сортировка модулей, перекомпиляция, перезагрузка).

таким образом, в Blackbox Component Pascal в некотором роде, есть прототип REPL.

можно поправить код — добавить новую процедуру, скомпилировать новую версию модуля, выгрузить старую ( + все его клиенты, если они есть), перезагрузить модуль, перезагрузить клиентов.

потом вызвать новую процедуру.

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

почти похоже на REPL, только REPL — это Hotpatching на уровне функций, а тут надо останавливать выполнение, перезагружать — на уровне модулей-компонент.

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

(or a b)

http://en.wikipedia.org/wiki/Tagged_union.

В CL это значит, что суммы нужно заворачивать в структуры:

(defstruct my-variant
  (variant nil :type (or number string)))

(defmacro with-my-variant ((var my-variant) for-number for-string)
  `(let ((,var (my-variant-variant ,my-variant)))
     (etypecase ,var
       (number ,for-number)
       (string ,for-string))))

(defmethod map-as-number ((my-variant my-variant) fn)
  (with-my-variant (var my-variant) (funcall fn var) var))

(defmethod map-as-string ((my-variant my-variant) fn)
  (with-my-variant (this my-variant) this (funcall fn this)))

(map-as-number (make-my-variant :variant 123) #'1+) ; => 124
(map-as-number (make-my-variant :variant "123") #'1+) ; => "123"
(map-as-string (make-my-variant :variant 123) #'length) ; => 123
(map-as-string (make-my-variant :variant "123") #'length) ; => 3

Следующим шагом будет написание макроса defunion так, чтобы

(defunion number-or-string number string)

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

ломает всю красивую картину ООП и делает диспетчеризацию по произвольным типам принципиально невозможной

Чем сишный union или boost::variant или std.variant из D ломает ООП? Если говорить про «наследники класса как варианты его типа», то sealed class hierarchy - и тоже ничего не ломает.

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

что-то вроде

Перем c;
Перем x=1;
Перем М[10];


Функция фыва(ы,ъ) {
  Перем й;
  Для й=0 по 3 Цикл
	х=ъ;
	М[й]=й+1;
 КонецЦикла;

фыва(с,x);

(VARDEF c)
(VARINIT x 1)
(VARARRAY M 10)

(FUNCDEF фыва (ы ъ)
	(BLOCK
		(VARDEF й)
		(FOR (й 0 3) 
		 (BLOCK
			(SETVAR x 3)
			(SETVAR М (й (+ й 1))) ) ) )
(CALLFUNC фыва (c x))
(defparameter c )
(defparameter х 1)
(defparameter M (make-array 10))

(defun фыва (ы ъ)
  (with-gensyms (й) 
	`(let ((,й nil))
	  @(loop for й from 0 to 3
		`(progn 
			(setf x 3)
			(setf (aref  М й) (+ й 1)) ) 
	          ))
   ))
anonymous
()
Ответ на: комментарий от anonymous
(CALLFUNC фыва (c x))

=>

(фыва с х)

сейчас просто парсер доделываю, вполне приличная грамматика на ANTLR получается, с тестами на gUnit.

скопипастил из старого лога.

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

с лиспом ещё не определился, пробую sbcl, abcl и gambit scheme.

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

Забавно, нам могло бы быть по пути, но у меня несколько другая цель. Мне не нужно, мммм, заимствовать код из 1С, но я хочу сделать язык, к-рый интегрируется с дебаггером Lispworks. Соответственно, должен порождаться код не в макросах, а сразу в примитивах лиспа, и без всяких gensym-ов. Интересна такая задача? Я начал делать нечто на базе javascript, вот тут разговор: http://lisper.ru/forum/thread/878

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

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

для понимания идеи, достаточно в общем-то: парсер на Java / ANTLR разбирает AST (есть тесты в gUnit), составляет AST и символьную таблицу, дампит AST через AST.toStringTree(). получается AST в S-выражениях. потом второй кусок загружается в лисп-среду с макросами, отдельный модуль - отдельным пакетом. разворачивается в третий кусок. компилируем пакет и зависимости => рабочая логика проведения документа, например.

то есть, сам код среды можно писать на том же лиспе или на 1С (который оттранслируется в тот же лисп). можно делать свои базовые объекты, ORM слой и движок транзакций.

ORM тоже можно распарсить и транслировать, как и формы, но это пока в проекте.

пока нет ORM, движка — есть парсер с юниттестами на gUnit, сборка через make, запуск оттранслированного через SLIME (системы модулей с подсистемами нормальной тоже нет).

в общем, пилится парсер just for fun.

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

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

в идеале можно хоть LLVM-биткод генерировать. сначала хотел обойтись StringTemplate шаблонами, но макросами гибче получается.

отпишусь если время будет.

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

boost::variant или std.variant

ой-ой, не хочу лезть в эти дебри даже.Не работал ни с boost, ни с std. Немного работал с Qt. И вообще, про С++ не хотелось бы даже вспоминать ближе к вечеру :) Слава Богам, что не приходится на нём программировать.

union в C - не ломает, т.к. и ломать нечего. Union вообще не содержит информации о типе, если её туда руками не вносить, какая тут диспатчеризация по типу?

Я имел в виду, что если нам пришёл number, а у нас два метода: один для number, другой - для number or string, то неясно, что делать. Это можно решить, но это вопрос выбора. Всплывают на память слова «type lattice», но я даже не знаю, что они значат. Я человек простой, пишу typecase и не парюсь больше.

Также у меня есть полуатомная бомба, под названием decorate-function, которая по сути есть setf (symbol-function ...) и протокол для хранения старой функции и её вызова. В Lispworks есть своё defadvice, но у меня переносимая реализация. Тем самым, можно для каждой (не обязательно родовой) функции иметь один around метод, этого хватает для очень широкого класса задач, когда недоступен исходник функции или не хочется его менять. А уже в этом around методе можно написать typecase и другие ветвления. Ломается оно только для функций, которые inline или имеют compiler-macro, но тут мы достигаем пределов, заданных природой и дальше всё равно не пройти. В Delphi можно сделать ещё больше за счёт code splicing - тогда можно подменять любой код, а не только точку входа в именованную функцию. Эту технологию я ещё в лиспе не освоил, да и в Delphi, по-моему, удалось без неё обойтись, хотя тесты на Дельфи я делал и они работали.

Может быть, стилисты и знатоки ООП покривятся, но меня этот подход удовлетворяет.

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

получается AST в S-выражениях
just for fun.

Да, именно just for fun с таким подходом. В ремесле понадобится от s-выражения найти исходную строку «1С»-ного кода, чтобы написать

«строка 20, колонка 10, неопределенная переменная TrueЪ»

Без этого не стоит надеяться завоевать пользователей. Я заранее нацеливаюсь решать не только эту проблему, но и возможность показать в дебаггере код «1С», вызвавший ошибку. Я уверен, что это возможно, и у меня есть необходимые куски, из которых лепить. Но я не уверен, что у меня есть на это ресурсы.

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

Спасибо за столь подробное описание BlackBox. Даже не знаю, что тут сказать. С одной стороны, гибкости явно не хватает без переопределения классов. С другой - ясно, что возможность переопределения стоит довольно дорого. С третьей, с точки зрения рилтайма получается, что у лиспа больше возможностей, ведь у него более мелкая гранулярность. С четвёртой, две иерархии зависимостей между interface и между implementation решают проблему циклических ссылок между модулями, которую в лиспе я решаю только костылями, хотя это, в общем-то, мелочь. С пятой, для максимальной производительности именно и нужно перегружать не отдельные функции, а модули - тогда можно будет гораздо глубже оптимизировать. С шестой, можно и в лиспе перегружать модули, и в Pascal делать очень мелкие модули. С седьмой, GCL реализован как раз через подгрузку динамических библиотек. С восьмой, у меня есть REPL для Дельфи, описанный выше, правда он не совсем полноценный, но его можно наращивать новыми типами и это нетрудно.

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

А каково количество пользователей этого BlackBox по сравнению с CL?

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

макросы также убивают возможность отследить место ошибки.

Почему это? Ошибки

(iter (for i in 1 10) (collect k)) =>  

Iterate, in (FOR I IN 1 10):
10 should be a symbol

(iter (for i from 1 to 10) (collect k)) =>

  The variable K is unbound.
0] backtrace

0: ((LAMBDA ()))
1: (SB-INT:SIMPLE-EVAL-IN-LEXENV
    (ITER
      (FOR I FROM 1 TO 10)
      (COLLECT K))
    #<NULL-LEXENV>)

так что всё зависит от автора макроса.

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

union в C - не ломает, т.к. и ломать нечего. Union вообще не содержит информации о типе, если её туда руками не вносить, какая тут диспатчеризация по типу?

Обычно (ну или иногда) union тегирован тегом типа, например:

#include <cstdio>

class IntOrString {
  public:
    enum { INT, STRING } tag;
    IntOrString(int i) : tag(INT) { content.as_int = i; }
    IntOrString(char *s) : tag(STRING) { content.as_string = s; }
    void operator=(int i) { tag = INT; content.as_int = i; }
    void operator=(char *s) { tag = STRING; content.as_string = s; }
    template <typename T> T get() const;
  private:
    union { int as_int; char *as_string; } content;
};

template <> const int* IntOrString::get() const
{
    return tag == INT ? &content.as_int : 0;
}

template <> const char* const* IntOrString::get() const
{
    return tag == STRING ? &content.as_string : 0;
}

#define typecase(X) switch ((X).tag)
#define begin_case(T) case (T) : {
#define end_case break; }

void test_it(const IntOrString &x)
{
    typecase (x) {

        begin_case (IntOrString::INT)
            const int *i = x.get<const int*>();
            if (i) printf("It is int: %d\n", *i);
        end_case

        begin_case (IntOrString::STRING)
            const char* const* s = x.get<const char* const*>();
            if (s) printf("It is string: %s\n", *s);
        end_case

    }
}

int main()
{
    IntOrString x = 123;
    test_it(x);
    x = "abc";
    test_it(x);
}

// ➜  ~  ./a.out                          
// It is int: 123
// It is string: abc

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

boost::variant это то же тегированный union, но с выносом типов объединяемых вещей в параметры типа:

#include <cstdio>
#include <string>
#include <boost/variant.hpp>

int main()
{
    // (or int string)
    typedef boost::variant<int, std::string> IntOrString;

    // typecase
    struct TestIt : boost::static_visitor<> {
        void operator()(int i) const { printf("It is int: %d\n", i); }
        void operator()(std::string &s) const { printf("It is string: %s\n", s.c_str()); }
    };

    IntOrString x = 123;
    boost::apply_visitor(TestIt(), x);
    x = "abc";
    boost::apply_visitor(TestIt(), x);
}

Получается проще и безопаснее.

Either в Haskell и Scala это такой же ADT:

test :: Either Int String -> IO ()
test = either (printf "This is int: %d\n") (printf "This is string: %s\n")

main :: IO ()
main = do
  let x = Left 123
  test x
  let x = Right "abc"
  test x

Это слишком простая вещь, чтобы она как-то смогла сломать ООП.

Вот у матчинга по типам и наследования могут быть проблемы, например:

trait IntOrString
case class MyInt(i: Int) extends IntOrString
case class MyString(s: String) extends IntOrString

object Test {

  def test(x: IntOrString) {
    x match {
      case MyInt(i) => println("This is int: " + i)
      case MyString(s) => println("This is string: " + s)
    }
  }

  def run {
    var x: IntOrString = MyInt(123)
    test(x)
    x = MyString("abc")
    test(x)
  }

}

// scala> Test.run
// This is int: 123
// This is string: abc

дописыванием

case class MyFoo() extends IntOrString

object Test2 {

  def run {
    var x: IntOrString = MyFoo()
    Test.test(x)
  }

}

// scala> Test2.run
// scala.MatchError: MyFoo() (of class MyFoo)

получаем исключение за счёт non-exhaustiveness в Test.run. Решается с помощью

- trait IntOrString
+ sealed trait IntOrString
- case class MyString(s: String) extends IntOrString
+ final case class MyString(s: String) extends IntOrString

Хотя в лиспе за non-exhaustiveness и исключения вообще не принято волноваться, так что там это и не проблема.

Я имел в виду, что если нам пришёл number, а у нас два метода: один для number, другой - для number or string, то неясно, что делать.

Очевидно, что для диспетчеризации (or number string) из type-level синонима должен превратиться в нормальный ADT (tagged union), так что number и (or number string) станут различимы в рантайме (тег там или боксинг - другой вопрос). Поэтому я и говорю «дорого». Таких вещей в стандартном CL не наблюдается - только предельный случай типа t и typecase.

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

Таких вещей в стандартном CL не наблюдается - только предельный случай типа t и typecase.

Буквально то же можно и здесь

(defclass int-or-string ()
  ((content :type (or number string)) :acessor get))

(defmethod test ((x int-or-string))
  (typecase (get x)
    (number (format t "This is int ~a~%" (get x)))
    (string (format t "This is int ~a~%" (get x)))))

Тэг всегда есть внутри значения. Его вообще можно всегда трактовать как

struct LispValue {
    int type-of;
  private:
    union { int as_int; char *as_string; ...; void *as_object } content;
};

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

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

На defstruct/defclass/deftype можно навелосипедить базовые A[lgebraic]DT (суммы, произведения, степени), но, например:

(defclass (tuple a b) ()
  ((content-a :type a :acessor get-a))
  ((content-b :type b :acessor get-b)))

(deftype int-and-string () (tuple int string))

;; (tuple a b c) etc. ?

(defclass (either a b) ()
  ((content :type (or a b) :acessor get)))

;; (either a b c) ?

(deftype int-or-string () (either int string))

(deftype my-printer (from) (function from string) ;; constraints? no wai! :)

?

Ну и вовсе оно не то же, в конечном итоге, потому что статических проверок, параметрического полиморфизма, sealed иерархий и exhaustiveness от лиспа добиться никак нельзя (?). То есть в динамике эти самые ADT получаются «глупые», но «мощные» (гетерогенные), тогда как в статике - «умные», но «бедные» (гомогенные), вопрос придания им мощности в статике это вопрос наличия (безопасного, «умного») аналога типа T.

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

...параметрического полиморфизма...

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

(defgeneric test-arr (obj element-type size))

не то?

статических проверок

Можно, но ограниченно. SBCL проверяет в момент определения, но можно сделать

(defun test1 (x)
  (+ x 1))

(defun test2 (x)
  (+ (test1 x) 1))

(defun test1 (x)
  (length x))
и статика уже не помогает. Каждая функция отдельно в момент определения корректна. Но весь кусок вместе некорректен. А отслеживать все места вызова с перекомпиляцией — долго.

sealed иерархий

Это с запретом наследования? Через metaclass тривиально. Также абстрактные классы и синглтоны.

exhaustiveness

Добиться можно. Если есть type inference. А в runtime штатно ecase, etypecase.

вопрос наличия (безопасного, «умного») аналога типа T.

А boost::variant не подходит? Или там нельзя указать «все типы».

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

макросы также убивают возможность отследить место ошибки.

Ты их готовить не умеешь. Рано тебе еще за Лисп браться.

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

test :: Either Int String -> IO ()
test = either (printf «This is int: %d\n») (printf «This is string: %s\n»)

Вот этот Either ни фига не (or ...). Потому как даже Either Int String <> Either String Int. И функции для работы с ним должны принимать конкретный Either, а не Int.

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

не то?

«Дерево элементы которого все обязательно одного типа - то есть не T, но и не какого-то конкретного, вроде int, просто какого-то типа-параметра a, главное, чтобы одного для всех» - примерно так. После чего с таким (гомогенным) контейнером можно работать обобщённо, и делать «дерево int-ов = то дерево c a = int». Ну или «дерево элементы которого все обязательно одного типа, про который также известно, что он реализует интерфейс <0, 1, +, *>» - можно работать обобщённо опираясь на этот интерфейс, потом «дерево int-ов = то дерево c a = int», так как для int он естественен, но не «дерево string-ов = то дерево c a = string» (пока такой интерфейс не определён для string).

Но это так или иначе требует статической типизации - если хочется различать Tree[Int] и Tree[String], просто как способ зафорсить какую-то логику в приложение, то система типов должна за этим следить - деревья не обходятся для проверки «ок, в Tree[Int] ничего лишнего не попало», просто данные индуктивно так устроены, что в Tree[A] всегда будут только A, это известно до запуска программы.

Ну а дальше уже и к фантомным типам можно придти

type Qual = { CanRead, CanWrite }

type PTree[T, Qual] = Tree[T]

sealed interface Read ...
PTree[T, _] implements final Read

sealed interface Write ...
PTree[T, CanWrite] implements final Write

или просто

function read(tree : PTree[T, _]) ...
function write(tree : PTree[T, CanWrite]) ...

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

SBCL проверяет в момент определения

Он проверяет, но это именно стилистические предупреждения. Настоящие статические проверки в статически типизированных языках это, фактически, мнение компилятора на счёт того как программист использует систему типов как инструмент («синтаксический фреймворк») для зашивания какой-то логики/инвариантов в программу. То есть если система типов - система, то есть инструмент с чёткой спецификацией, то статические проверки это такой outcome от его использования. Насколько хорош этот инструмент - другой вопрос, компилятора с его проверками это не волнует, - программист как-то должен подстраиваться под систему типов и учиться утилизировать её по назначению.

Это с запретом наследования?

С разрешением наследования в одной синтаксической области, чтобы населить тип/иерархию, и с запретом везде кроме неё, чтобы добиться возможности проверять exhaustiveness.

А boost::variant не подходит?

boost::any, scala.Any. Ну и даже lispobj из SBCL как тегированный указатель - но он только для своих, то есть для рантайма SBCL.

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

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

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

Не понимаю в чем может быть фан от попыток натянуть свою
задачу на какой-нибудь убогий язык

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

разве нужно и полезно кому нибудь писать ещё
один веб-фреймворк?

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

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

Вот этот Either ни фига не (or ...).

И функции для работы с ним должны принимать конкретный Either, а не Int.

Так и должно быть. Оборачивать синоним or в структуру [3].

Потому как даже Either Int String <> Either String Int.

Есть изоморфизм (<f, t, and, or> это коммутативное полукольцо). То есть вопрос в том как ты понимаешь type equality.

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

«строка 20, колонка 10, неопределенная переменная TrueЪ»

http://shmat-razum.blogspot.ru/2011/11/racket.html

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

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

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

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

Не вам одному, поэтому программистов и интересует тот инструмент с которым они работают (не всех, конечно).

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

Странно это слышать от лиспера, а как же концепт — каждой задаче свой язык в предметной области задачи? Для меня Лисп это больше метаязык, нежели язык общего назначения.

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

но и возможность показать в дебаггере код «1С», вызвавший ошибку

дебаггер там настолько идиотский, что проще написать свой

anonymous
()

Я бы попробовал сделать статически-типизированный лисп с полноценной фазой компиляции, на которой только и можно было бы совершать макромагию. Плюс возможность определять синтаксис поверх s-expr. В результате бы получился этакий универсальный конструктор языков... Делаем DSL'и для предметных областей и реализуем задачи просто и ясно.

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

С одной стороны, гибкости явно не хватает без переопределения классов

какой гибкости, в каком месте ? каких классов? «расширяемые записи», то есть, классы, там есть.

С другой - ясно, что возможность переопределения стоит довольно дорого.

не понял, поясни примером. что переопределять и почему дорого?

--

ещё добавлю: с девятой стороны, в BBCP активно используется метапрограммирование. например,

1. GUI : построение форм через «итеракторы» — скомпилированный модуль разбирается на предмет экспортированных переменных и функций, по которым автоматически строится форма;

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

3. пример в исходниках BBCP, как реализован «отладчик» (окошко с trap-ом, выскакивающее по остановам) — обычного отладчика там нет by design.

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

например, какого-то лиспа поверх BBCP :)) вот и идея-фикс#1

лучше, видимо, остаться на том, что есть

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

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

другое дело, если какой-то парсер-транслятор применить, с дельфи на блекбокс, но его ещё написать надо. вот и идея-фикс#2

А каково количество пользователей этого BlackBox по сравнению с CL?

cложно сказать. может и больше общелиспового. почти все тусуются на forum.oberoncore.org, информатика21, форуме про ДРАКОН-технологии.

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

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