Глава 5 Lisp Webtales - как заставить это работать - PullRequest
2 голосов
/ 06 октября 2019

Исходя из моего решения, чтобы получить код в главе 4, работающий как здесь , мне нужна помощь в отладке приложения "linkdemo" в главе 5 lisp webtales .

Я тщательно набрал весь код и устранил одну опечатку, содержащуюся в книге.

В CCL если я выдаю (ql:quickload "linkdemo"), я получаю это:

CCL is free software.  It is distributed under the terms of the Apache
Licence, Version 2.0.
? (ql:quickload "linkdemo")
To load "linkdemo":
  Load 1 ASDF system:
    linkdemo
; Loading "linkdemo"
.
;;; Checking for wide character support... yes, using code points.
;;; Checking for wide character support... yes, using code points.
;;; Building Closure with CHARACTER RUNES
.
> Error: The value NIL is not of the expected type STRING.
> While executing: ENSURE-SIMPLE-STRING, in process listener(1).
> Type :POP to abort, :R for a list of available restarts.
> Type :? for other options.
1 > 

Если я наберу :R, я получу это:

1 > :R
>   Type (:C <n>) to invoke one of the following restarts:
0. Return to break level 1.
1. #<RESTART ABORT-BREAK #x133AEDD>
2. Retry compiling #P"/Users/m/quicklisp/local-projects/linkdemo/defmodule.lisp"
3. Skip compiling #P"/Users/m/quicklisp/local-projects/linkdemo/defmodule.lisp"
4. Retry compiling #<CL-SOURCE-FILE "linkdemo" "defmodule">.
5. Continue, treating compiling #<CL-SOURCE-FILE "linkdemo" "defmodule"> as having been successful.
6. Retry ASDF operation.
7. Retry ASDF operation after resetting the configuration.
8. Give up on "linkdemo"
9. Return to toplevel.
10. #<RESTART ABORT-BREAK #x133C53D>
11. Reset this thread
12. Kill this thread
1 > 

, из которого я делаю вывод, что ошибка в defmodule.lisp.

Затем я обнаружил, что если я закомментирую следующее полноеблокировать в defmodule.lisp:

(restas:define-policy datastore
  (:interface-package #:linkdemo.policy.datastore)
  (:interface-method-template "DATASTORE-~A")
  (:internal-package #:linkdemo.datastore)

  (define-method init ()                                    
    "Initiate the datastore")

  (define-method find-user (username)                       
    "Find the user by username")

  (define-method auth-user (username password)
    "Check if a user exists and has the supplied password")

  (define-method register-user (username password)
    "Register a new user")

  (define-method upvoted-p (link-id username)
    "Check if a user has upvoted a link")

  (define-method upvote (link-id user)
    "Upvote a link")

  (define-method post-link (url title user)
    "Post a new link")

  (define-method get-all-links (&optional user)
    "Get all of the links in the datastore")

  (define-method upvote-count (link-id)
    "Get the number of upvotes for a given link"))

, затем переиздать (ql:quickload "linkdemo"), я не получаю ошибки.

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

Независимо от того, я быстро загрузил с или без нарушающий разделвыше, я могу исследовать функцию в pg-datastore.lisp, например, так:

1 > (linkdemo.pg-datastore::hash-password "42")
(:PASSWORD-HASH "71a8c8f54475acb5afee9eae061fa5f7ba838215f280259855e59a7c0ac768f8" :SALT "a283c5328f67c36595dd7277c54342f3")

Пока не представляется возможным попытаться загрузить сервер, так как кодиз главы 6, прежде чем он станет полностью функциональным (у нас пока нет маршрутов, только слой DAO с postmodern).

Так что не знаете, как приступить к тестированию кода в гл.5 / отладке этого. Ошибка, кажется, что-то в самом restas - может быть.

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

Ошибка может быть воспроизведена простым выполнением (ql:quickload "restas") с последующей оценкой данного блокавыше. Это дает Error: The value NIL is not of the expected type STRING. Я новичок в использовании кода других в LISP, поэтому я немного застрял в том, как действовать.

Дополнительная информация : это вызовы define-method, которые выдают эту ошибку, и источником является файл policy.lisp здесь , который сейчас немного за мной.

Я пробовал sbcl тоже:

* (ql:quickload "linkdemo")
To load "linkdemo":
  Load 1 ASDF system:
    linkdemo
; Loading "linkdemo"
.
;;; Checking for wide character support... WARNING: Lisp implementation doesn't use UTF-16, but accepts surrogate code points.
 yes, using code points.
;;; Building Closure with CHARACTER RUNES
.........
debugger invoked on a SB-KERNEL:CASE-FAILURE in thread
#<THREAD "main thread" RUNNING {10004F04C3}>:
  NIL fell through ETYPECASE expression.
  Wanted one of (SIMPLE-STRING STRING SB-FORMAT::FMT-CONTROL).

...

(SB-FORMAT::%FORMAT #<SB-IMPL::CHARACTER-STRING-OSTREAM {1003BD1673}> NIL ("UPVOTE-COUNT") ("UPVOTE-COUNT"))
0] 

1 Ответ

4 голосов
/ 07 октября 2019

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

(restas:define-policy datastore
  (:interface-package #:linkdemo.policy.datastore)
  (:interface-method-template "DATASTORE-~A")
  (:internal-package #:linkdemo.datastore)
  (define-method init ()                                    
    "Initiate the datastore"))

Проблема здесь в том, что при развертывании вышеупомянутого макроса вы получаете вызов функции в RESTAS::%DEFINE-POLICY, где :INTERNAL-FUNCTION-TEMPLATEзадано NIL, что не является подходящей строкой формата. Это связано с тем, что макрос define-policy анализирует параметры.

Вот сокращенный пример:

(defun my-function (&key (x 0))
  (print x))

MY-FUNCTION имеет значение по умолчанию 0 для :x. Если вы вызываете (my-function), он печатает 0. Но макрос делает что-то вроде этого:

(defmacro my-macro (args)
  (let ((x-arg))
    (loop for (k v) in args 
       do (case k (:x (setf x-arg v))))
    `(my-function :x ,x-arg)))

Он перебирает список аргументов, чтобы проверить, существует ли какое-либо ключевое слово для аргумента :x, и затем устанавливаетлокальная переменная (инициализируется в ноль) к соответствующему значению. Во всех случаях он расширяется до вызова my-function и явно связывает x со значением x-arg, даже если он равен нулю. И действительно, если мы макроэкспандировать (my-macro ()), если дает (my-function :x nil).

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

(defmacro my-macro (args)
  (flet ((maybe (k v) (and v (list k v))))
    (let ((x-arg))
      (loop for (k v) in args do (case k (:x (setf x-arg v))))
      `(my-function ,@(maybe :x x-arg)))))

Оператор сращивания ,@ используется для введения ключевого словааргументы в вызове, основанные на том, является ли значение ноль или нет. Когда это ноль, пустой список объединен. В противном случае список свойств (list k v) склеивается. И, таким образом, вызов (my-macro ()) теперь расширяется до (my-function), и, таким образом, x будет принимать значение по умолчанию.

Это не очень хорошо проверено, но я думаю, вы можете переопределить define-policy следующим образом:

(defmacro define-policy (name &body body)
  (let (methods
        internal-package  internal-function-template
        interface-package interface-method-template)
    (iter (for item in body)
          (case (car item)
            (:internal-package
             (setf internal-package (second item)))
            (:internal-function-template
             (setf internal-function-template (second item)))
            (:interface-package
             (setf interface-package (second item)))
            (:interface-method-template
             (setf interface-method-template (second item)))
            (otherwise
             (cond
               ((string= (car item) "DEFINE-METHOD")
                (push (cdr item) methods))
               (t
                (error "Unknown DEFINE-POLICY option: ~A" item))))))
    (flet ((maybe (key value) (and value (list key value)))
           (maybe/quote (key value) (and value (list key `(quote ,value)))))
      `(eval-when (:compile-toplevel :load-toplevel :execute)
         (%define-policy ',name ',methods
                         ,@(maybe/quote :interface-package interface-package)
                         ,@(maybe :interface-method-template interface-method-template)
                         ,@(maybe/quote :internal-package internal-package)
                         ,@(maybe :internal-function-template internal-function-template))))))

В качестве альтернативы просто добавьте аргумент :internal-function-template (например, "~A") в ваш макрос.


Исправлено: https://github.com/archimag/restas/commit/81bbbab6b36f81f846f78e71232e9d3d15f6d952

...