LINUX.ORG.RU

Лисперы, что я делаю не так? - II


2

3

Я понял что я делал не так — я просто не использовал лисп.

Вощем, может кому полезно будет, человек пишет как дошел до жизни такой http://www.defmacro.org/ Честно говоря это мой уже не первый подход к снаряду. До этого раза поташнивало, сейчас думаю зашел. Действительно лисп оказался прост как грабли.

Без матов осознание того на сколько оно кульно описать тяжко.

Есть вопросы:

1) Кто-нибудь есть кто пользует vim, при этом пишет на лиспе?

2) Ясен хобот емакс мощнее, но как то оно не то. Есть кто пересел? Как ощущения вообще?

3) Practical Common Lisp ослилил. Что есть еще для просветления?

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

То есть да, все дело в ф-и walk, вот только вызывать эту ф-ю надо в самом экспандере. Просто подставив это дело в макрос такое сделать никак не получится.

А почему так? Если перечислять фичи гигиены, то будет примерно так:

1) Брать переменные из скопа определения. Это просто - в функции walk заменять связанные символы (symbol?) на их значения (symbol-value), вызывать ошибки для неопределённых символов.

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

3) Вызывать walk во время экспанда? Есть *macroexpand-hook* из CLTL2.

4) Вот ссылочно-прозрачные макросы, т.е. syntax-case по AST это несколько сложнее, для этого волкер функции walk должен быть AST ориетированный, с возможностью AST матчинга (фактически, частный случай ADT и pattern matchingа).

expand там переписывается.

С нуля :) Ну то есть не с нуля, но трюкачество - и в pp там реализация с нуля на бедной схеме.

Что такое «произвольный кодеволкер»?

Функция с контрактом

Expression                                     ;; исходная форма
Environment                                    ;; окружение исходной формы
Expression, Context, Environment -> Expression ;; функция от субформы исходной формы, соответсвующего контекста и окружения
-> Expression                                  ;; форма результат

т.е. с помощью функции в третьем аргументе можно как угодно оценивать и видоизменять субформы исходной формы, причём учитывая их контекст и окружение. В CL просто проблемы с хорошим кодволкером, тем не менее есть sb-walker:walk-form именно с таким контрактом. Если в scheme кодволкер уже есть в макросистеме, то, по идее, написать такого рода walk-form должно быть не сложно?

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

> 1) Брать переменные из скопа определения. Это просто - в функции walk заменять связанные символы (symbol?) на их значения (symbol-value), вызывать ошибки для неопределённых символов.

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

3) Вызывать walk во время экспанда? Есть *macroexpand-hook* из CLTL2.

Ну да, а можно вообще экспандер переопределить :) Таким макаром можно из CL сделать любой язык, вообще.

4) Вот ссылочно-прозрачные макросы, т.е. syntax-case по AST это несколько сложнее, для этого волкер функции walk должен быть AST ориетированный, с возможностью AST матчинга (фактически, частный случай ADT и pattern matchingа).

Там нету никакого матчинга по АСТ, это обычный патерн-матчинг списков. ну или парсинг атрибутных грамматик с параметризованными правилами, если речь о syntax-parse.

С нуля :) Ну то есть не с нуля, но трюкачество - и в pp там реализация с нуля на бедной схеме.

Ну вот о чем и речь.

Функция с контрактом

Ну expand такой контракт и имеет, см. local-expand. И в макросе можно захватить текущий контекст (в котором макрос вызван), то есть оно тоже передается, но неявно.

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

Как ты значение возьмешь?

Это я из того примера о трёх строках так понял - делаем (define x 5) и дальше в макросе x всегда == 5, иначе - почему тогда (let ([x 10]) (macro)) => 5? Этим достигается ссылочная прозрачность.

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

А это уже как в CL получается, т.е. негигиенично (пункт 2). Или нет?

Там нету никакого матчинга по АСТ, это обычный патерн-матчинг списков.

Тогда это тоже «петушение списков» :) Вместо ADT в качестве AST. Впрочем, такие ADT вложимы в списки, так что ладно.

Ну expand такой контракт и имеет, см. local-expand.

Т.е. если отдать expand форму и функцию identity по первому аргументу, то expand вернёт исходную форму безо всяких изменений? (т.е. не будет ничего там раскрывать). Если нет, то это не чистый кодволкер, а его уселение - экспандер с функциями кодволкера. Не то, короче.

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

> Это я из того примера о трёх строках так понял - делаем (define x 5) и дальше в макросе x всегда == 5, иначе - почему тогда (let ([x 10]) (macro)) => 5?

ну так а: (let ((x 10)) (macrolet *тут замыкаемся на х*)) замыкание только на топлевел это костыль. тем более - а если мы в файле это пишем, а не в репле? Вообще, закладываться на то, что код будет исполняться исключительн ов репле - нельзя.

А это уже как в CL получается, т.е. негигиенично (пункт 2). Или нет?

Конечно, нет. Откуда там берется негигиеничность?

Тогда это тоже «петушение списков»

в «петушение списков» самом по себе нету ничего плохого, плохо, когда списки прходится петушить вручную :)

Т.е. если отдать expand форму и функцию identity по первому аргументу, то expand вернёт исходную форму безо всяких изменений? (т.е. не будет ничего там раскрывать). Если нет, то это не чистый кодволкер, а его уселение - экспандер с функциями кодволкера. Не то, короче.

Что-то я не совсем понял. Если тебе не нужно вызывать родной expand, то тогда такой кодеволкер - это просто макрос :) Ну или phase-1 функция, ты ее сам можешь написать.

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

в тред элегантно входит С++0x

И тут же уползает назад, униженный и оскорбленный тем, что для него нет и никогда не будет полноценного REPL.

держи две: c-repl — на базе GCC-XML.hs и ccons на базе clang и LLVM

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

>Ведь в зависимости от языка может кому-то оказаться не нужным отладчик, другим профайлер, третьим быстродействие.

Что бы получить полноценный REPL язык должен проектироваться соответствующим образом.


вообще-то да. Вот например, можно прикрутить REPL к C (ccons), только толку-то от него? Из исключений восстановиться нельзя, сегфолты не откатить, и т.п.

Сейчас я знаю о существовании двух таких языков - CL (плюс некоторые другие диалекты лисп) и Smalltalk.


Оберон-2 BlackBox Component Pascal:
http://www.inr.ac.ru/~info21/blackbox/start/welcome.html#commander
http://www.inr.ac.ru/~info21/blackbox/dev/zagruzka.htm#kogda




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

> Сейчас я знаю о существовании двух таких языков - CL (плюс некоторые другие диалекты лисп) и Smalltalk.

ещё можно Factor добавить

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

Что бы получить полноценный REPL

Кстати, чем полноценный REPL отличается от неполноценного?

хороший вопрос. Вот GDB чем не REPL?

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

ну если на коленке, то вот есть такой бейсик под винду — LangMF

 Dim Buf(24)
  Dim t1, t2, b

  ' This is Asm Code
  '
  ' 55                     push    ebp
  ' 8BEC                   mov     ebp,esp
  ' 8B4D08                 mov     ecx,[ebp+8]
  ' E30D                   jecxz   @getout
  ' 33C0                   xor     eax,eax
  ' @start:
  ' 49                     dec     ecx
  ' 83F900                 cmp     ecx,0
  ' 75FA                   jnz     @start
  ' B801000000             mov     eax,1
  ' @getout:

  Buf(0) = &H55
  Buf(1) = &H8B
  Buf(2) = &HEC
  Buf(3) = &H8B
  Buf(4) = &H4D
  Buf(5) = &H8
  Buf(6) = &HE3
  Buf(7) = &HD
  Buf(8) = &H33
  Buf(9) = &HC0
  Buf(10) = &H49
  Buf(11) = &H83
  Buf(12) = &HF9
  Buf(13) = &H0
  Buf(14) = &H75
  Buf(15) = &HFA
  Buf(16) = &HB8
  Buf(17) = &H1
  Buf(18) = &H0
  Buf(19) = &H0
  Buf(20) = &H0
  Buf(21) = &H5D
  Buf(22) = &HC2
  Buf(23) = &H8
  Buf(24) = &H0

  sys.dynapi.curbuf = 0
  sys.dynapi.setbuf(buf)

  b=100000000


  t1 = sys.api.gettickcounta
  rc = sys.dynapi.callfunction2(vbNullString, vbNullString, sys.dynapi.ptrbuf, 2, b)
  t2 = sys.api.gettickcounta

вопрос, чем вот такой типа-FFI не REPL? или, если POSIX C, компилируем в librepl.so, выгружаем dlclose(«старый librepl.so»), загружаем новый откомпилированный dlopen(«новый librepl.so»), инициализируем и запускаем через dlsym ?

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