Lisp Webtales 第 5 章 - 如何让它工作
Lisp Webtales Chapter 5 - how to get it working
按照我的解决方案使第 4 章中的代码作为 here, I need some help debugging the application "linkdemo" in Chapter 5 of 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")
,没有报错。
(此外,如果我再次在 in 中评论它,并使用快速加载重新加载,我也不会收到任何错误。我不确定那里发生了什么或原因.)
无论我是否快速加载了 有或没有 上面有问题的部分,我都可以在 pg-datastore.lisp
中探索一个函数,如下所示:
1 > (linkdemo.pg-datastore::hash-password "42")
(:PASSWORD-HASH "71a8c8f54475acb5afee9eae061fa5f7ba838215f280259855e59a7c0ac768f8" :SALT "a283c5328f67c36595dd7277c54342f3")
似乎还不能尝试启动服务器,因为在它完全发挥作用之前需要第 6 章的代码(我们还没有路由,只有带 postmodern
的 DAO 层)。
所以不确定如何继续测试第 5 章中的代码/调试它。
错误似乎是 restas 本身的问题 - 也许吧。
寻求关于如何进行的任何有根据的猜测。
可以通过简单地执行 (ql:quickload "restas")
然后评估上面给出的块来重现错误。这给了 Error: The value NIL is not of the expected type STRING.
我是在 LISP 中使用其他人的代码的新手,所以我有点不知道如何继续。
更多信息:是对 define-method
的调用导致此错误,源是文件 policy.lisp
here , 目前有点超出我的能力范围。
我也试过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]
我可以在只定义一个方法的情况下重现测试用例:
(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
对 :x
的默认值为 0。如果调用 (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
参数的某些关键字是否存在,然后将局部变量(初始化为nil)设置为相应的值。
在所有情况下,它都会扩展为对 my-function
的调用,并显式地将 x
与 x-arg
的值绑定,即使它为 nil。事实上,如果我们宏展开 (my-macro ())
,如果给出 (my-function :x nil)
.
由于函数已经定义了默认值,我们希望避免在其他地方重复这个默认值,因此宏应该注意不要在其对应值为 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)))))
,@
拼接运算符用于根据值是否为 nil 将关键字参数注入调用。为nil时,拼接空列表。否则拼接属性列表(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
按照我的解决方案使第 4 章中的代码作为 here, I need some help debugging the application "linkdemo" in Chapter 5 of 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")
,没有报错。
(此外,如果我再次在 in 中评论它,并使用快速加载重新加载,我也不会收到任何错误。我不确定那里发生了什么或原因.)
无论我是否快速加载了 有或没有 上面有问题的部分,我都可以在 pg-datastore.lisp
中探索一个函数,如下所示:
1 > (linkdemo.pg-datastore::hash-password "42")
(:PASSWORD-HASH "71a8c8f54475acb5afee9eae061fa5f7ba838215f280259855e59a7c0ac768f8" :SALT "a283c5328f67c36595dd7277c54342f3")
似乎还不能尝试启动服务器,因为在它完全发挥作用之前需要第 6 章的代码(我们还没有路由,只有带 postmodern
的 DAO 层)。
所以不确定如何继续测试第 5 章中的代码/调试它。 错误似乎是 restas 本身的问题 - 也许吧。
寻求关于如何进行的任何有根据的猜测。
可以通过简单地执行 (ql:quickload "restas")
然后评估上面给出的块来重现错误。这给了 Error: The value NIL is not of the expected type STRING.
我是在 LISP 中使用其他人的代码的新手,所以我有点不知道如何继续。
更多信息:是对 define-method
的调用导致此错误,源是文件 policy.lisp
here , 目前有点超出我的能力范围。
我也试过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]
我可以在只定义一个方法的情况下重现测试用例:
(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
对 :x
的默认值为 0。如果调用 (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
参数的某些关键字是否存在,然后将局部变量(初始化为nil)设置为相应的值。
在所有情况下,它都会扩展为对 my-function
的调用,并显式地将 x
与 x-arg
的值绑定,即使它为 nil。事实上,如果我们宏展开 (my-macro ())
,如果给出 (my-function :x nil)
.
由于函数已经定义了默认值,我们希望避免在其他地方重复这个默认值,因此宏应该注意不要在其对应值为 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)))))
,@
拼接运算符用于根据值是否为 nil 将关键字参数注入调用。为nil时,拼接空列表。否则拼接属性列表(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