LINUX.ORG.RU

Кто хотел лисп, компилирующийся в C++?

 , ,


0

3

Нашёл на просторах Интернета: https://bitbucket.org/ktg/l/src/337c13802c5e?at=master

Умеет макросы

(define-syntax sum
  (syntax-rules ()
    [(sum) 0]
    [(sum a) a]
    [(sum a b) (+ a b)]
    [(sum a b ...) (+ a (sum b ...))]))

(define-syntax-rule (infix a op b) (op a b))

(define-syntax-rule (ret a) (return a))

(defmacro unless (pred a b)
  `(if (not ,pred) ,a ,b))

(main
  (prn (sum 1 2 3 4))
  (prn (infix 1 + 2))
  (unless false (prn "Will print") (prn "Will not print"))
  (ret 1))

Примеры смотреть в https://bitbucket.org/ktg/l/src/337c13802c5e/ex/?at=master

Хвалите и критикуйте!

★★★★★

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

Вот это С:

int main()
{
for (i = 0; i < 10; i++)
   printf("%d", i);
}

Пытаемся сделать аналог на ECL:

$ cat > test.lisp
(dotimes (i 10) (print i))
$ ecl -c test.c -compile test.lisp
$ cat test.c
 .... поскипано много сттрок инициализации ...
 ECL_DEFINE_SETF_FUNCTIONS
  {
   cl_fixnum v1i;
   v1i = 0;
   goto L4;
L3:;
   ecl_print(ecl_make_fixnum(v1i),ECL_NIL);
   v1i = (v1i)+1;
L4:;
   if (!((v1i)<(10))) { goto L9; }
   goto L3;
L9:;
  }
}

Ну вот. Ты хочешь сказать, что это быстро выполняется? Или это читабельно? Или и то и другое? В общем случае SBCL работает быстрее, чем ECL

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

вот пробовал я въехать в этот лисп, понять что оно такое и зачем нужно, статейки всякие читал - и всё никак
кто на нем программиурет вообще?

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

Ты хочешь сказать, что это быстро выполняется? Или это читабельно?

* (macroexpand '(dotimes (i 10) (print i)))

(BLOCK NIL
  (LET ((I 0))
    (DECLARE (TYPE UNSIGNED-BYTE I))
    (TAGBODY
      (GO #:G627)
     #:G626
      (TAGBODY (PRINT I))
      (PSETQ I (1+ I))
     #:G627
      (UNLESS (>= I 10) (GO #:G626))
      (RETURN-FROM NIL (PROGN NIL)))))

Те же яйца, вид сбоку. А всё потому, что макросы - зло.

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

Ты хочешь сказать, что это быстро выполняется?

Вполне быстро

Или это читабельно?

У тебя задача - любоваться получеными C-листинги? Или таки выполнять код? Если первое, то да, ECL тебе не подходит. И как верно заметили выше: твой входной CL-код далеко не финальный - даже в рамках самого CL, он раскрывается конструкцию, весьма похожую на выхлоп ECL'а на C.

В общем случае SBCL работает быстрее, чем ECL

А в Киеве дядько.

Sectoid ★★★★★
()
Ответ на: комментарий от no-such-file

А всё потому, что макросы - зло.

Почему же зло? Вполне себе инструмент. Свою задачу выполняет весьма неплохо.

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

Почему же зло?

Потому что мешают оптимизации. Вон dotimes тупо разорачивается в goto. После чего компилятору нужно понять что это цикл, догадаться где условие выхода и тд

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

Вон dotimes тупо разорачивается в goto. После чего компилятору нужно понять что это цикл, догадаться где условие выхода и тд

Таков стандарт CL. Хотя, наверное, можно было бы для случаев, когда не указан tag, не «разворачиваться» в label/goto. Но скорее это усложнение.

Потому что мешают оптимизации.

Надо смотреть результирующий асм-код. И, опять таки, это беда не всех макросов, а конкретно ECL'овской реализации dotimes.

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

ECL — это полноценный CL (довольно убогий в плане реализации), а сабж — просто еще один С++ на скобках.

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

вот пробовал я въехать в этот лисп, понять что оно такое и зачем нужно, статейки всякие читал - и всё никак
кто на нем программиурет вообще?

Лисперы

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

ECL — это полноценный CL (довольно убогий в плане реализации), а сабж — просто еще один С++ на скобках.

И именно поэтому результат компиляции сабжа будет в 2-3 раза быстрее.

У лиспа есть два неустранимых препятствия против производительности:

1. GC

2. динамическая типизация (что влечёт боксинг)

У CL ещё есть требование запуска дебаггера при ошибке, что в свою очередь аналогично тому, что весь код в C++'ном try/catch.

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

У CL ещё есть требование запуска дебаггера при ошибке, что в свою очередь аналогично тому, что весь код в C++'ном try/catch.

Есть же

(declare (optimize (safety ...)))
который в надежде на +2% скорости позволяет ронять все в чисто С-шном стиле с шумом и грохотом.

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

У лиспа ... неустранимых препятствия против производительности:
1. GC

В принципе let-формы размечают границы регионов в правильном коде. И тут вопрос больше в неидеальности компиляторов чем в самом CL как стандарте.

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

В смысле, руками? Ты про конструирование списков через defmacro?

Нет, я про:

Именно тут нельзя. Эти макросы не выполняются в среде Racket, а раскрываются через (expand-to-top-form (eval `(syntax ,lst) ns))

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

anonymous
()

Эм, лиспом же ни кто не пользуется, нафиг оно нужно?

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

Ты хочешь сказать, что это быстро выполняется?

Не он, но да: Его:

        leaq	3(,%rbx,4), %rdi
	movl	$1, %esi
	addq	$1, %rbx
	call	ecl_print
	cmpq	$10, %rbx
	jne	.L4

Твоё:

.L2:
	movl	%ebx, %esi
	movl	$.LC0, %edi
	xorl	%eax, %eax
	call	printf
	addl	$1, %ebx
	cmpl	$10, %ebx
	jne	.L2

Если бы ecl писали не не криволапые макаки застрявшие в 80-х, а норм пацаны, то гцц бы выдал абсалютно такой же код, а так он не может заинлайнить ecl_print.

anonymous
()

Хочу наобарот. С++->Lisp

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

В принципе let-формы размечают границы регионов в правильном коде.

Если от лиспа оставить только let, тогда да. Но это равносильно запрету в C++ использовать delete (а оставить только переменные на стеке и RAII).

Да и с let:

(defun foo (x)
  (let ((y (list x)))
     (cons y (list 2))))

Как думаешь, что будет, если при выходе из формы очистится память из под y?

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

Есть же escape analysis, который сможет сказать, вытекает ли переменная за пределы let-формы (или вообще lambda-формы).

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

Твоя мама вкусный борщ варит? Если нет, то лишп не для тебя.

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

For и while в Си тоже разворачиваются в goto. А найти back edge в cfg потом легче легкого. Умели бы компиляторы лишпиков в SSA, проблем с оптимизацией не было бы.

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

Есть же escape analysis, который сможет сказать, вытекает ли переменная за пределы let-формы (или вообще lambda-формы).

Ну так у тебя все эти переменные стекутся в верхний регион и забьют хип.

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

От лиспа потеряна библиотека во время выполнения (так как С++ не умеет вызывать лисповые функции), но это в данном случае часть постановки задачи.

Ну если оно реально кому-то нужно, то ладно. Это я скорее уже сам себе нафантазировал так как хотелось бы иметь что-то типа С++ с макросами. Или статически типизированный и компилирующийся лисп. Последнее (очень) условно есть, но хотелось бы в виде «полноценной реализации».

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

Умели бы компиляторы лишпиков в SSA

Ну некотоыре схемы в CPS умеют, так что SSA какбы искаробки.

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

Есть же escape analysis, который сможет сказать, вытекает ли переменная за пределы let-формы

(defun foo (x)
 (let ((y (list x)))
   (bar y)))

(setf (fdefinition bar) #'car)

(foo 1)

(setf (fdefinition bar) #'list)

(foo 2)

Должен ли foo освобождать память, занимаемую y ? Или выкинем всю динамичность из CL (и получим в лучшем случае C++ со скобками)? Или прикрутим JIT, который будет при каждом вызове функции анализировать её тело? Тогда уж дешевле смириться с GC

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

Нет, конечно. foo понятия не имеет, что это за bar, так что никаких оптимизаций относительно неё выполняться не должно. Escape analysis применим только к статически известным функциям. Он позволяет избежать работы с GC, если писать программы, удобные для оптимизатора.

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

Или статически типизированный и компилирующийся лисп.

Проблема со стандартной библиотекой. Какой тип имеет, например, mapcar ?

monk ★★★★★
() автор топика
Ответ на: комментарий от monk
> (require typed/racket)
> map
- : (All (c a b ...) (case-> ((a -> c) (Pairof a (Listof a)) -> (Pairof c (Listof c))) ((a b ... b -> c) (Listof a) (Listof b) ... b -> (Listof c))))
#<procedure:map>
> 
anonymous
()
Ответ на: комментарий от monk

И именно поэтому результат компиляции сабжа будет в 2-3 раза быстрее.

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

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

Хотя, если использовать его исключительно в качестве генератора

Это и есть генератор, по факту.

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

(require typed/racket)

> (require typed/racket)
> (map cons '(1 2 3 4) '(5 6 7 8))
. Type Checker: Polymorphic function `map' could not be applied to arguments:
Types: (a b ... b -> c) (Listof a) (Listof b) ... b -> (Listof c)
       (a -> c) (Pairof a (Listof a)) -> (Pairof c (Listof c))
Arguments: (All (a b) (case-> (a (Listof a) -> (Listof a)) (a b -> (Pairof a b)))) (List One Positive-Byte Positive-Byte Positive-Byte) (List Positive-Byte Positive-Byte Positive-Byte Positive-Byte)
Expected result: AnyValues
 in: (map cons (quote (1 2 3 4)) (quote (5 6 7 8)))

вот поэтому и нет нормального типизированного лиспа. Либо это лисп, тогда типизация местами, либо типизация везде и под это переделать стандартную библиотеку, но тогда получится Haskell.

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

Хотя, если использовать его исключительно в качестве генератора, вполне неплохо

А как его ещё можно использовать? Генератор и замена препроцессора.

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

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

Lisp

(+ 5 2.3) 

Typed Lisp

(FunCall (Id '+) 
         (Args (Cons<Arg> (Arg (IntLit 5)) 
                          (Cons<Arg> (Arg (FloatLit 2.3)) 
                                     (Nil<Arg>)))))

Я упоротый и не правильно понимаю гомоиконность?

Kuzy ★★★
()
Ответ на: комментарий от monk
> (require typed/racket)
> (map (inst cons Integer Integer) '(1 2 3) '(4 5 6))
- : (Listof (Pairof Integer Integer))
'((1 . 4) (2 . 5) (3 . 6))
> 

тут проблема не в типизации, а в недоделанном инференсе

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

кстати:

> (cons "x" (cdr '(1)))
- : (Listof String)
'("x")
в то время как какой-нибужь жалкий хачкиль КЕННОТ ИНТУ

а в 6.1 вроде еще и фильтры есть

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

Я упоротый и не правильно понимаю гомоиконность

Упоротый. То что ты попытался нарисовать — это синтаксические конструкторы (например в Scala и Nemerle они так выглядят, но даже там есть квазицитирование).

Проблема не в языке. Typed Racket для функций в стиле Haskell работает замечательно. Проблема в стандартной библиотеке и совместимости.

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

а в недоделанном инференсе

У меня периодически возникают сомнения в алгоритмической разрешимости нормального инференса для той системы типов, что наворотили в Racket.

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

(Listof String) и (Listof Int) должны быть разные Nil-ы

С чего вдруг? Та еще скажи, что в С++ на каждый MyClass* должен отдельный NULL быть.

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

Ну хз, так же такая МОЩНАЯ СИСТЕМА ТИПОВ, а на практике вон - слепленный на коленке типизированный лиспег оказывается в плане типизации мощнее :trollface.jpg:

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

То что ты попытался нарисовать — это синтаксические конструкторы (например в Scala и Nemerle они так выглядят, но даже там есть квазицитирование).

синтаксические конструкторы

Что это? Давай условимся в терминах.

Есть выражение

(+ 5 2.7)

Это ты называешь «синтаксическим конструктором» в Lisp-е? (код ниже)

(cons '+ (cons '5 (cons '2.7 '())))

Или это?

(+ . (5 . (2.7 . ())))

Я в контексте Typed Lisp имел ввиду второй вариант (наверно слово конструктор здесь не подходит, да).

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

Нарушение типизации, чего радуешься?

Нет, никакого нарушения. Вот так тебе лучше будет понятно, что происходит:

> (require typed/racket)
> (map + '(1))
- : (Listof One) [more precisely: (Pairof One (Listof One))]
'(1)
> (cdr (map + '(1)))
- : (Listof One)
'()
> (cons "x" (cdr (map + '(1))))
- : (Listof (U String One))
'("x")
> (car (cons "x" (cdr (map + '(1)))))
- : (U String One)
"x"
> 

Просто в первом случае компилятор выводит более точный тип.

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

У меня периодически возникают сомнения в алгоритмической разрешимости нормального инференса для той системы типов, что наворотили в Racket.

Ну хуйней типа полной разрешимости и т.п. пусть страдают хачкелисты, которые систему типов испоганят, но лишь бы ФУЛЛ ИНФЕРЕНС ЙОБА. Пусть будет неполный, пусть надо будет подсказывать в некоторых редких случаях - но многие случаи (по крайней мере с map'ом) инференс точно можно сделать.

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

Та еще скажи, что в С++ на каждый MyClass* должен отдельный NULL быть.

Должен. Ты же не будешь сравнивать:

MyClass* a = nullptr;
MyClass2* b = nullptr;
if(a == b) {
   
}

Скорее всего это ошибка.

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

там не NULL, а пустой список

уже в начальной школе объясняют, что пустое множество и отсутсвие множества - разные вещи

не пались, тёлки давать не будут, решат что школьник

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