LINUX.ORG.RU

Common Lisp: eval-when

 ,


2

1

Мужики, поясните за eval-when. В целом, эмпирически, я представляю как работает eval-when в разрезе compile-file, (load .lisp) и (load .fasl), но таблички в HyperSpec-е и CLtL2 вообще никак соотнести со своими представлениями не могу. В частности, не могу понять что из-себя compile-time-too и non-compile-time режимы представляют и когда они наступают. В описании какие-то мутные, если можно так сказать, определения.

Для удобства, приведу таблички здесь: http://www.lispworks.com/documentation/HyperSpec/Body/03_bca.htm

| CT  | LT  | E   | Mode | Action   | New Mode         |
|-----+-----+-----+------+----------+------------------|
| Yes | Yes | -   | -    | Process  | compile-time-too |
| No  | Yes | Yes | CTT  | Process  | compile-time-too |
| No  | Yes | Yes | NCT  | Process  | non-compile-time |
| No  | Yes | No  | -    | Process  | non-compile-time |
| Yes | No  | -   | -    | Evaluate | -                |
| No  | No  | Yes | CTT  | Evaluate | -                |
| No  | No  | Yes | NCT  | Discard  | -                |
| No  | No  | No  | -    | Discard  | -                |

https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node68.html#SECTION00933000000...

| LT  | CT  | EX  | CTTM | Action                                |
|-----+-----+-----+------+---------------------------------------|
| yes | yes | -   | -    | process body in compile-time-too mode |
| yes | no  | yes | yes  | process body in compile-time-too mode |
| yes | no  | -   | no   | process body in non-compile-time mode |
| yes | no  | no  | -    | process body in non-compile-time mode |
| no  | yes | -   | -    | evaluate body                         |
| no  | no  | yes | yes  | evaluate body                         |
| no  | no  | -   | no   | do nothing                            |
| no  | no  | no  | -    | do nothing                            |

Статью (на русском) Fare про eval-when знаю, но считаю её ещё более запутанной, чем описания в первоисточниках.

P.S. У меня вот такие таблички получились:

| :compile-toplevel | :load-toplevel | compile-file   | load .fasl |
|-------------------+----------------+----------------+------------|
| -                 | -              | -              | -          |
| +                 | -              | eval           | -          |
| -                 | +              | compile        | eval       |
| +                 | +              | eval & compile | eval       |

| :execute | load .lisp     |
|----------+----------------|
| -        | -              |
| +        | eval           |
Это всё для случая, когда eval-when на верхнем уровне. В подформах для eval-when имеет смысл только :execute. Если стоит — обрабатываем, если нет — nil.



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

Не очень понимаю, что тут разного?

Какой префикс должен быть у функции, которая работает с функциями? f, fun, function-, ...? Это просто например.

ad-hoc решения, навроде fset, вполне можно использовать не очень напрягаясь

Так в том-то и дело. Мощная система макросов вкупе со слабой стандартной библиотекой приводит к тому, что люди переписывают defmacro, так как стандартный бойлерплейт с gensym неудобен, переписывают вообще def.... А кончается всё это языком, выглядящим как:

{with-quasi-quoted-xml-to-binary-emitting-form-syntax/preserve-whitespace
 (def layered-function render-source-list (first instance)
   (:method :in xhtml-layer (first (instance source-text:source-list))
     <span (:class "list") "(" ,(foreach #'render-source-object (source-text:source-sequence-elements instance)) ")">)

   (:method ((first source-text:source-symbol) (instance source-text:source-list))
     (render-source-list (source-text:source-symbol-value first) instance))

   (:method :in xhtml-layer ((first (eql 'def)) (instance source-text:source-list))
     (bind ((elements (source-text:source-sequence-elements instance)))
       <span (:class "definition")
             "("
             <span (:class "def") ,(render-source-object-text (pop elements))>
             ,(render-source-object (pop elements))
             <span (:class "kind") ,(render-source-object-text (pop elements))>
             ,(render-source-object (pop elements))
             <span (:class "name") ,(render-source-object-text (pop elements))>
             ,(foreach #'render-source-object elements)
             ")">)))}

Или (в другом варианте) так:

(defun make-piecewize-linear-scalar-fun (value-table &key allow-extrapolation (pseudo-name "Untitled piecewize linear scalar fun"))
  "Возвращает функцию одного аргумента, представляющую из себя кусочно - линейную скалярную ф-ю, по данным. Создаёт ссылку на данные - их нельзя разрушать. Данные должны быть упорядочены по x и их должно быть не менее 2 штук. Если allow-extrapolation, то за границами использует экстраполяцию из крайнего отрезка"
  (perga
    (:lett vt mjr_mat value-table)
    (:lett rows fixnum (mjr_mat_rows value-table))
    (assert (> rows 1))
    (assert (= (mjr_mat_cols value-table) 2))
    (let current-x nil)
    (let min-x nil)
    (dotimes (i rows)
     (:lett new-x number [ vt i 0 ])
     (cond 
       ((null current-x) (setf min-x new-x))
       (t 
        (assert (> new-x current-x) () "Value table must be ordered at point ~A" new-x)))
     (setf current-x new-x)
     )
    (let max-x current-x)
    (lambda (x) 
      (unless allow-extrapolation
        (assert (<= min-x x max-x) () "Extrapolation is forgiven for ~A" pseudo-name))
      (piecewize-linear-scalar-fun x vt))))

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

результатом будет T, только что проверил

М-да при компиляции T:

* (load "test")

T
* test::*wild*

:WILD

По-моему load не возвращает последний параметр. Ладно:

$ cat > test.lisp
(defpackage :test
  (:use :cl :asdf))

(in-package :test)
(defvar *wild* t)
(format t "~a" *wild*)
$ sbcl
* (load "test.lisp")
WILD
T

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

у меня SBCL(1.2.4), CCL(1.10) и LispWorks(6) возращают T.

Этот код при выполнении тоже печатает T ?

(defpackage :test
  (:use :cl :asdf))

(in-package :test)
(defvar *wild* t)
(format t "~a" *wild*)

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

Какое то извращение.

И я про то же. Если в Racket крупные проекты вполне читаемы, то на CL крупные проекты почти всегда вырождаются в такое «извращение».

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

на CL крупные проекты почти всегда вырождаются в такое «извращение».

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

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

Только что посмотрел на сырцы LispWorks'овского IDE (выйдет за большой проект? 3Мб в исходниках) — выглядит тоже аккуратно и легко читается в рамках стандартного CL, без вышепоказанного извращения.

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

...А кончается всё это языком, выглядящим как:

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

Или (в другом варианте) так:

ден73 тайно злоупотребляет тяжёлыми наркотиками

какие-то маргинальные примеры приводишь

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

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

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

остальные пункты или не существенны, или дело привычки

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

Да, на всех вышеперечисленных реализациях.

Значит у тебя другая версия asdf.

Что выводит asdf::*asdf-version* ? У меня «2.26».

Ну попробуй такой вариант:

(defpackage :test
  (:use :cl :asdf))

(in-package :test)
(defvar *central-registry* t)
(format t "~a" *central-registry*)

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

остальные пункты или не существенны, или дело привычки

Ладно. Убедил. Хотя «маргинальных примеров» почему-то на лиспе гораздо больше. Причём даже в виде книг: let-over-lambda, например.

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

Так в том-то и дело. Мощная система макросов вкупе со слабой стандартной библиотекой приводит к тому, что люди переписывают defmacro, так как стандартный бойлерплейт с gensym неудобен, переписывают вообще def....

Пфф... В действительности with-gensyms и once-only всё покрывает. Ну конечно не возбраняется улучшать что-нибудь себе под руку, как например в том же LOL:

(defmacro defmacro/g! (name args &rest body)
  (let ((syms (remove-duplicates
                (remove-if-not #'g!-symbol-p
                               (flatten body)))))
    `(defmacro ,name ,args
       (let ,(mapcar
               (lambda (s)
                 `(,s (gensym ,(subseq
                                 (symbol-name s)
                                 2))))
               syms)
         ,@body))))
Но обычно этим, кроме особых эстетов, никто не страдает, а просто решают задачу.

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

А кончается всё это языком, выглядящим как:

Ты привёл какой-то откровенный говнокод, который, кстати, совершенно не сообразен тезису о том, что «нужно в CL все def* переписывать».

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

В Racket есть Swindle

Сыровато выглядит. Это вообще кто-нибудь использует?

но любой алгоритм с сигнальным протоколом я могу переписать в стиле Scheme/Racket практически в тот же объём строк.

Хм. Дело-то не в алгоритмах, а в предоставлении интерфейса в выборе стратегии исправления ошибок выше по стеку. Я понимаю, что с продолжениями можно сделать всё тоже самое, но это не удобно и использованиt продолжений в схеме для тех же целей что и использование сигналов в CL не в ходу.

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

но любой алгоритм с сигнальным протоколом я могу переписать в стиле Scheme/Racket практически в тот же объём строк

Ну если не лень, зафигач гипотетичесий парсер логов из PCL http://www.gigamonkeys.com/book/beyond-exception-handling-conditions-and-rest....

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

Fare весь в слезах от использования Java и Питона. ;)

Да, как бы, вся суть Fare.

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

Racket работает практически на той же скорости, что и SBCL

Верится с трудом. Да что там, практически не верится. Есть где-нибудь замеры?

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

Какой префикс должен быть у функции, которая работает с функциями? f, fun, function-, ...?

Ну это же ни о чём. Экая борьба за арийскую чистоту. Я почти уверен, что большинство наименований взяты из стека предков CL и привнесены в стандарт дабы не вызывать недоумение у знатоков. Можно покопаться по деталькам в истории, но лениво.

deadlock
() автор топика

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

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

На Racket нет крупных проектов.

web-server

sirmail — почтовый клиент

DrRacket — текстовый редактор с возможностью вставки изображений в текст

racket/gui

slideshow

honu — язык программирования

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

Какое то извращение.

Первое из dwim.hu, второе судя по стилю имени den73. Вполне обычный для них код.

Другой вопрос почему настолько нерядовые явления, а не какой-ниюудь iterate или optima того же Fare. Раз уж вспомнили про dsl.

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

Racket работает практически на той же скорости, что и SBCL

Верится с трудом. Да что там, практически не верится. Есть где-нибудь замеры?

Вот сравнение с SBCL c (safety 0)

http://benchmarksgame.alioth.debian.org/u32/racket.php

Я делал микробенчмарк на

(require racket/unsafe/ops)

(define (test)
  (letrec ([oddp (λ (x) (if (zero? x) #f
                       (evenp (unsafe-fx- x 1))))]
            [evenp (λ (x)
                    (if (zero? x) #t
                        (oddp (unsafe-fx- x 1))))])
    (oddp 400000000)))
(time (test))
cpu time: 1109 real time: 1110 gc time: 93

Против

(declaim (optimize (speed 3) (safety 1) (debug 0)))
(defun test ()
           (labels ((oddp1 (x) (declare (fixnum x))
                      (if (zerop x) nil
                          (evenp1 (1- x))))
                    (evenp1 (x) (declare (fixnum x))
                      (if (zerop x) t
                          (oddp1 (1- x)))))
             (oddp1 400000000)))
(time (test))
Evaluation took:
  1.705 seconds of real time

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

Ну если не лень, зафигач гипотетичесий парсер логов

К вечеру сделаю.

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

я когда-то давно пробовал Racket немного, но мне что-то не понравилось; вроде бы отладчик показался каким-то мудацким

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

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

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

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

Потому что лисп — это такой пластилин, лепи что хочешь. Freedom comes with responsibilty так сказать, и в извращениях неких инопланетян CL не виноват.

Причём даже в виде книг: let-over-lambda, например.

Абуза макросов приводит к извращениям. :)

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

зафигач гипотетичесий парсер логов из PCL

#lang racket
(define (parse-log-entry text [#:on-error #f])
  (cond 
    [(well-formed-log-entry? text) (make-log-entry ...)]
    [on-error (on-error text 
                        (λ (value) value) 
                        (λ (fixed-text) (parse-log-entry fixed-text)))]
    [t (error 'malformed-log-entry-error "~a" text)]))


(defun log-analyzer ()
  (define (on-error text use-value reparse-entry)
    (use-value (make-malformed-log-entry text)))
  (for/list ([log (find-all-logs)])
    (analyze-log log on-error)))
monk ★★★★★
()
Ответ на: комментарий от monk
(define (analyze-log log on-error)
  (for/list ([entry (parse-log-file log on-error)])
    (analyze-entry entry)))
monk ★★★★★
()
Ответ на: комментарий от Oxdeadbeef

Выводит значение asdf:*central-registry* — путь к системам, как и должно быть.

Так и я про то же. А где в коде

(in-package :test)
(defvar *central-registry* t)
(format t "~a" *central-registry*)
видно, что *central-registry* возьмётся из asdf (описание пакета всё-равно всегда в отдельном файле)? То есть результат работы программы молча меняется в зависимости от версии используемой asdf. Вот представь себе, написал человек такой код в то время, когда в экспорте asdf ещё не было *central-registry*. Всё работало. Потом asdf обновляется на минорную версию и добавляет один символ в экспорт (так как существующие символы не удаляются и не меняют своё поведение, то версия считается полностью совместимой). И у кого-то программа внезапно перестают работать, причём глядя на код почти невозможно понять, что произошло.

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

Freedom comes with responsibilty так сказать, и в извращениях неких инопланетян CL не виноват.

Виноват. Тем, что своими возможностями провоцирует извращения. Я поэтому и привёл в пример высказывания Линуса про С++. С++ тоже ведь не виноват, что на нём выбирают не самые лучшие алгоритмические решения:

C++ leads to really really bad design choices. You invariably start using the «nice» library features of the language like STL and Boost and other total and utter crap ... In other words, the only way to do good, efficient, and system-level and portable C++ ends up to limit yourself to all the things that are basically available in C.

Это же относится к CL и Racket. И именно поэтому тот же Swindle в Racket почти никто не использует. В 95% задач это overkill, а накладные расходы есть всегда. Кроме того CLOS провоцирует незакрытые модули (в смысле в одном модуле определили generic, а в другом доопределили для какого-то частного случая и в зависимости от наличия второго модуля, программа работает по разному).

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

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

Полный импорт стороннего пакета, кроме #:cl, плохая идея, поэтому нужно знать, что делаешь. Во вторых, обычно следят за основноми изменениями API используемых библиотек — никто в здравом уме не будет добавлять символ в экспорт в минорных апдейтах.

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

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

Так для данной задачи продолжения не нужны.

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

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

hunchentoot. Обновление 1.2.28 на 1.2.29. Добавлен hunchentoot:*close-hunchentoot-stream*

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

Виноват. Тем, что своими возможностями провоцирует извращения

Боишься выстрела в ногу? выбери что нибудь по-безопаснее, но менее удобное.

И именно поэтому тот же Swindle в Racket почти никто не использует.

Потому что это нестандартная фича и nobody cares? CLOS мощен, стандартен и хорошо оптимизирован — поэтому используют, там где нужно. Но опять же могут быть инопланетяне сующие его куда попало.

и в зависимости от наличия второго модуля, программа работает по разному

Для этого есть правила и гайдлайны по разработке софта.

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

Обновление 1.2.28 на 1.2.29. Добавлен hunchentoot:*close-hunchentoot-stream*

Ну редиски значит. Опять же кто будет делать (:use #:hunchentoot)? ССЗБ?

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

правила и гайдлайны по разработке софта

Где гайдлайн по CLOS, где написано, что нельзя доопределять метод на подмножестве типа существующего метода? Наоборот, AFAIK, это рассматривается как достоинство CLOS. :around вообще провоцирует такое написание (до тех пор, пока в одном образе не встретится два :around на одну комбинацию типов).

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

Так для данной задачи продолжения не нужны.

Вроде началось с того, как в ракетке сделать «эмуляция сигнального протокола с продолжениями». :)

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

Причем здесь CLOS? Я вообще о проектировании софта говорил. Есть некоторые фичи и сопутствующие грабли, и с учетом этого нужно знать что делаешь.

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

Вроде началось с того, как в ракетке сделать «эмуляция сигнального протокола с продолжениями»

Было

любой алгоритм с сигнальным протоколом я могу переписать в стиле Scheme/Racket практически в тот же объём строк. А если учесть наличие call/cc, который позволяет не сразу вызывать рестарт, а чуть позже, так всё ещё удобней (особенно для Web'а).

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

Есть некоторые фичи и сопутствующие грабли

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

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

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

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

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