Why Common Lisp CLOS matches on a method where the arguments are the wrong class? - common-lisp

I am trying to learn CLOS and bumped into this surprise. I have those 3 action methods that seemingly incorrectly match on my arguments. When I run tryme function, why the action marked with the last argument being ':c' does not trigger the expected error? What am I doing wrong? Or is it an SBCL bug?
(eval-when (:compile-toplevel :load-toplevel :execute)
(ql:quickload '(alexandria serapeum defclass-std)))
;; (load "~/AAA//clos-turnstile.lisp")
(in-package "CL-USER")
(shadowing-import 'defclass-std::defclass/std)
;;; -----------------------------------------------------------------------------------------
(defclass/std current-state () ())
(defclass/std locked (current-state) ())
(defclass/std unlocked (current-state) ())
(defclass/std input () ())
(defclass/std icoin (input) ())
(defclass/std ipush (input) ())
(defclass/std turnstile ()
((state :std (make-instance 'locked))))
(defgeneric action (turnstile state i msg)
(:documentation "action for our FSM"))
(defmethod action ((turnstile turnstile) (any-state T) (any-input T) (msg T))
(error "0 unmatched action for ~S ~S ~a" any-state any-input msg))
(defmethod action ((turnstile turnstile) (locked current-state) (i icoin) (msg T))
(warn "1 lock coin unlock ~S" msg))
(defmethod action ((turnstile turnstile) (unlocked current-state) (i ipush) (msg T))
(warn "2 unlock push lock ~s" msg))
(defparameter *turnstile* (make-instance 'turnstile))
(defun show-me ()
(format t "=== ~S~%" *turnstile*))
(defun tryme ()
(show-me)
(action *turnstile* (make-instance 'locked) (make-instance 'icoin) :a)
(show-me)
(action *turnstile* (make-instance 'unlocked) (make-instance 'ipush) :b)
(show-me)
;; why this does not give the error? I have swapped the argument types.
(action *turnstile* (make-instance 'unlocked) (make-instance 'icoin) :c)
(show-me)
(action *turnstile* (make-instance 'locked) (make-instance 'icoin) :d)
(show-me)
(action *turnstile* (make-instance 'unlocked) (make-instance 'ipush) :e)
(show-me)
*turnstile*)
Suspicion
I suspect I should have:
(locked locked) instead of (locked current-state)
in the method definition, but how do I avoid such mistakes?

In what you expected to be an error, you were calling action on objects of classes turnstile, unlocked and icoin (and :c which is a keyword, but none of your methods specializes on this argument). But unlocked is a subclass of current-state ! So the call matches
(defmethod action ((turnstile turnstile) (locked current-state) (i icoin) (msg T))
(warn "1 lock coin unlock ~S" msg))
Remember that in a defmethod lambda-list, each specialized argument has the form (var-name class-name) (or some other combinations, e.g. using eql or classes themselves rather than their name ...)
Hence,
(defmethod action ((turnstile turnstile) (locked current-state) (i icoin) (msg T))
(warn "1 lock coin unlock ~S" msg))
(defmethod action ((turnstile turnstile) (unlocked current-state) (i ipush) (msg T))
(warn "2 unlock push lock ~s" msg))
only differ on their third argument (ipush/icoin), and not the current-state.

With the modified arguments of the method definitions, I get the expected error.
(defmethod action ((turnstile turnstile) (any-state T) (any-input T) (msg T))
(error "0 unmatched action for ~S ~S ~a" any-state any-input msg))
(defmethod action ((turnstile turnstile) (locked locked) (i icoin) (msg T))
(warn "1 lock coin unlock ~S" msg))
(defmethod action ((turnstile turnstile) (unlocked unlocked) (i ipush) (msg T))
(warn "2 unlock push lock ~s" msg))

Related

'Required argument is not a symbol' error in let binding

In the following code, I get a Required argument is not a symbol error.
(defconstant +localhost+ (vector 127 0 0 1))
(defun ip-from-hostname (hostname)
(sb-bsd-sockets:host-ent-addresses
(sb-bsd-sockets:get-host-by-name hostname)))
(defun test-connect
(let ((ip (car (ip-from-hostname "www.google.com")))
(socket (make-instance 'sb-bsd-sockets:inet-socket :type :stream :protocol :tcp)))
(sb-bsd-sockets:socket-bind socket +localhost+ 8080)
(sb-bsd-sockets:socket-connect socket ip)
(sb-bsd-sockets:socket-send socket "GET / HTTP/1.1" nil)
(write-line (sb-bsd-sockets:socket-receive socket nil 2048))))
(test-connect)
More complete error message:
Required argument is not a symbol: ((IP
(CAR
(IP-FROM-HOSTNAME "www.google.com")))
(SOCKET
(MAKE-INSTANCE
'SB-BSD-SOCKETS:INET-SOCKET :TYPE
:STREAM :PROTOCOL :TCP)))
I've narrowed down the issue to the section calling ip-from-hostname, but the strange thing is a boiled down version of the let binding works in the REPL:
(let ((ip (sb-bsd-sockets:host-ent-addresses (sb-bsd-sockets:get-host-by-name "www.google.com"))))
(write-line (write-to-string (car ip))))
I also tried replacing the ip-from-hostname call with its body thinking that it might be something to do with the arguments, but still no luck. Any thoughts?
(defun test-connect ...
... should start with a lambda list (the list of parameters), which is missing.
Remember, the syntax for DEFUN is:
defun function-name lambda-list
[[declaration* | documentation]]
form*

Cannot get input stream working in SBCL sb-ext:run-program

While the following works:
(let* ((i (make-string-input-stream "foo bar baz"))
(p (sb-ext:run-program "/bin/cat" '()
:input i :output *trace-output* :wait t)))
(sb-ext:process-close p))
the code below does not - it will stop after writing "001":
(let* ((_1 (format t "001~%"))
(p (sb-ext:run-program "/bin/cat" '()
:input :stream :output *trace-output* :wait t))
(_2 (format t "010~s~%" p))
(s (sb-ext:process-input p)))
(declare (ignore _1 _2))
(format s "foo bar baz~%")
(finish-output s)
(sb-ext:process-close p))
So it seems to silently leave execution in sb-ext:run-program.
This is with SBCL 1.3.6 on Ubuntu 16.04.1.
Any ideas? Thanks in advance, Frank
As I mentioned in the comments, the problem is the :WAIT T argument. It causes the call to SB-EXT:RUN-PROGRAM to not return until the child process exits.
In the first example you passed a string input stream to the child process. cat will read input from the stream, and when the input ends there will be a End of File, so cat exits. In the second example there is no input available for the program, so it's effectively an infinite loop (just like if you run cat on the command line, and don't give any input to it; it will never exit).
The solution is to use :WAIT NIL. You will also have to close the input stream with CLOSE, because otherwise there will be no EOF and cat keeps listening for more input. You'll also want to use SB-EXT:PROCESS-WAIT after closing the stream to wait for cat to exit itself.
(let* ((p (sb-ext:run-program "/bin/cat" '()
:input :stream
:output *standard-output*
:wait nil))
(s (sb-ext:process-input p)))
(format s "foo bar baz~%")
(finish-output s)
(close s)
(sb-ext:process-wait p)
(sb-ext:process-close p))
I'm not sure why you used *TRACE-OUTPUT* for the child output, so I changed it to *STANDARD-OUTPUT*.
Also, using FORMAT for debugging like that is kind of ugly. Common Lisp provides actual debugging tools. In this case you could use STEP:
(step (let* ((p (sb-ext:run-program "/bin/cat" '()
:input :stream
:output *standard-output*
:wait nil))
(s (sb-ext:process-input p)))
(format s "foo bar baz~%")
(finish-output s)
(close s)
(sb-ext:process-wait p)
(sb-ext:process-close p)))
This will put you in the debugger, showing the call being evaluated next. You can invoke the STEP-NEXT-restart to continue to the next call.
This is what works, as suggested by jkiiski:
(let* ((p (sb-ext:run-program "/bin/cat" '()
:input :stream
:output *standard-output*
:wait nil))
(s (sb-ext:process-input p)))
(format s "foo bar baz~%")
(finish-output s)
(sb-ext:process-wait p)
(sb-ext:process-close p))

Clojurescript async <? macro

I keep seeing this macro <?, in swanodette's code which looks really useful :
In this gist :
;; BOOM!!! we can convert async errors into exceptions
(go (try
(let [x (<? (run-task (.-readFile fs) "foo.txt" "utf8"))]
(.log js/console "Success" x))
(catch js/Error e
(.log js/console "Oops" e))))
In this blog post :
(go (try
(let [tweets (<? (get-tweets-for "swannodette"))
first-url (<? (expand-url (first (parse-urls tweets))))
response (<? (http-get first-url))]
(. js/console (log "Most recent link text:" response)))
(catch js/Error e
(. js/console (error "Error with the twitterverse:" e)))))
<? is just a touch of macro sugar that expands into something like
(throw-err (<! [expr])). In core.async <! serves the same purpose as
ES6's yield operator. If an asynchronous process writes an error onto
its channel we will convert it into an exception.
But I can't find a definition for it. How is it implemented in Clojure{Script} ?
Alright so here is what I am using so far. There is probably room for improvement.
In Clojure :
(defn throw-err [e]
(when (instance? Throwable e) (throw e))
e)
(defmacro <? [ch]
`(throw-err (<! ~ch)))
In ClojureScript :
(defn error? [x]
(instance? js/Error x))
(defn throw-err [e]
(when (error? e) (throw e))
e)
(defmacro <? [ch]
`(throw-err (<! ~ch)))
I am completely unsure about the readability of my solution though (throw-err looks like it should throw an error, but it doesn't. At least not every time).

How to reduce code duplication using method combination but keeping possible early return

I got a set of classes which represent a message that has to be handled. But there is only a limited amount of open spots for handlers. Therefore any "dispatch" of a handler handling an message object has to check first whether there is a free spot.
If there is -> dispatch.
If there is not -> do not dispatch and return corresponding message
As this part of the code will be the same in any dispatch method I figured it would be best to use the method combination facility to enforce that, but I cannot figure out how.
In my current code base I tried to use a :before method, but apparently you cannot use return in such context:
(defclass message () ((msg :initarg :msg :reader msg)))
(defclass message-ext (message)
((univ-time :initarg :univ-time :reader univ-time)))
(defparameter *open-handler* nil)
(defgeneric handle (message)
(:documentation "handle the given message appropriately"))
(defmethod handle :before ((message message))
(when (> (length *open-handler*) 1)
(return :full)))
(defmethod handle ((message message))
(push (FORMAT nil "dispatched handler") *open-handler*))
(defmethod handle ((message-ext message-ext))
(push (FORMAT nil "dispatched ext handler") *open-handler*))
(handle (make-instance 'message :msg "allemeineentchen"))
(handle (make-instance 'message-ext
:msg "rowrowrowyourboat"
:univ-time (get-universal-time)))
(handle (make-instance 'message-ext
:msg "gentlydownthestreet"
:univ-time (get-universal-time)))
Execution of a form compiled with errors.
Form:
(RETURN-FROM NIL FULL)
Compile-time error:
return for unknown block: NIL
[Condition of type SB-INT:COMPILED-PROGRAM-ERROR]
Restarts:
0: [RETRY] Retry SLIME interactive evaluation request.
1: [*ABORT] Return to SLIME's top level.
2: [TERMINATE-THREAD] Terminate this thread (#<THREAD "worker" RUNNING {100594F743}>)
Backtrace:
0: ((SB-PCL::FAST-METHOD HANDLE :BEFORE (MESSAGE)) #<unavailable argument> #<unavailable argument> #<unavailable argument>)
1: ((SB-PCL::EMF HANDLE) #<unavailable argument> #<unavailable argument> #<MESSAGE-EXT {1005961733}>)
2: (SB-INT:SIMPLE-EVAL-IN-LEXENV (HANDLE (MAKE-INSTANCE 'MESSAGE-EXT :MSG "gentlydownthestreet" :UNIV-TIME (GET-UNIVERSAL-TIME))) #<NULL-LEXENV>)
3: (EVAL (HANDLE (MAKE-INSTANCE 'MESSAGE-EXT :MSG "gentlydownthestreet" :UNIV-TIME (GET-UNIVERSAL-TIME))))
4: ((LAMBDA () :IN SWANK:INTERACTIVE-EVAL))
Is this approach even sane, and if yes how can I do it in a working fashion? (I did already try return-from with the same result)
I think you should be using the :around method qualifier instead:
(defmethod handle :around ((message message))
(if (cddr *open-handler*)
:full
(call-next-method)))
However, a more "lispy" approach is to use the CL Condition System, e.g., something like this:
(define-condition too-many-messages (...) (...) ...)
(defun add-message (message)
(when (cddr *open-handler*)
(signal 'too-many-messages))
(push message *open-handler*))
(defmethod handle ((message message))
(add-message (FORMAT nil "dispatched handler")))
You will have to handle the condition (using, e.g., handler-bind) in addition to checking the return values of your handle function.
PS. Calling length on a list to check that it is long enough is not a very good idea - although in your case, when the list is guaranteed to be short, this might be more of a style issue.
PPS. It is not a very good idea to use the word handle as a name of your function because CL has functions which contain it (e.g., handler-case). This will complicate the search in your code in addition to confusing people reading your code.
You can't call RETURN to return from a function like that.
You would need to use RETURN-FROM with the function name. But here it would return from the method - not the generic function.
#sds has an answer. Another one would be to signal a user defined condition and handle it somewhere else. Older code used catch and throw.
A more complex undertaking would be a user defined method combination.

How to interact with a process input/output in SBCL/Common Lisp

I have a text file with one sentence per line. I would like to lemmatize the worlds in each line using hunspell (-s option). Since I want to have the lemmas of each line separately, it wouldn't make sense to submit the whole text file to hunspell. I do need to send one line after another and have the hunspell output for each line.
Following the answers from How to process input and output streams in Steel Bank Common Lisp?, I was able to send the whole text file for hunspell one line after another but I was not able to capture the output of hunspell for each line. How interact with the process sending the line and reading the output before send another line?
My current code to read the whole text file is
(defun parse-spell-sb (file-in)
(with-open-file (in file-in)
(let ((p (sb-ext:run-program "/opt/local/bin/hunspell" (list "-i" "UTF-8" "-s" "-d" "pt_BR")
:input in :output :stream :wait nil)))
(when p
(unwind-protect
(with-open-stream (o (process-output p))
(loop
:for line := (read-line o nil nil)
:while line
:collect line))
(process-close p))))))
Once more, this code give me the output of hunspell for the whole text file. I would like to have the output of hunspell for each input line separately.
Any idea?
I suppose you have a buffering problem with the program you want to run. For example:
(defun program-stream (program &optional args)
(let ((process (sb-ext:run-program program args
:input :stream
:output :stream
:wait nil
:search t)))
(when process
(make-two-way-stream (sb-ext:process-output process)
(sb-ext:process-input process)))))
Now, on my system, this will work with cat:
CL-USER> (defparameter *stream* (program-stream "cat"))
*STREAM*
CL-USER> (format *stream* "foo bar baz~%")
NIL
CL-USER> (finish-output *stream*) ; will hang without this
NIL
CL-USER> (read-line *stream*)
"foo bar baz"
NIL
CL-USER> (close *stream*)
T
Notice the finish-output – without this, the read will hang. (There's also force-output.)
Python in interactive mode will work, too:
CL-USER> (defparameter *stream* (program-stream "python" '("-i")))
*STREAM*
CL-USER> (loop while (read-char-no-hang *stream*)) ; skip startup message
NIL
CL-USER> (format *stream* "1+2~%")
NIL
CL-USER> (finish-output *stream*)
NIL
CL-USER> (read-line *stream*)
"3"
NIL
CL-USER> (close *stream*)
T
But if you try this without the -i option (or similar options like -u), you'll probably be out of luck, because of the buffering going on. For example, on my system, reading from tr will hang:
CL-USER> (defparameter *stream* (program-stream "tr" '("a-z" "A-Z")))
*STREAM*
CL-USER> (format *stream* "foo bar baz~%")
NIL
CL-USER> (finish-output *stream*)
NIL
CL-USER> (read-line *stream*) ; hangs
; Evaluation aborted on NIL.
CL-USER> (read-char-no-hang *stream*)
NIL
CL-USER> (close *stream*)
T
Since tr doesn't provide a switch to turn off buffering, we'll wrap the call with a pty wrapper (in this case unbuffer from expect):
CL-USER> (defparameter *stream* (program-stream "unbuffer"
'("-p" "tr" "a-z" "A-Z")))
*STREAM*
CL-USER> (format *stream* "foo bar baz~%")
NIL
CL-USER> (finish-output *stream*)
NIL
CL-USER> (read-line *stream*)
"FOO BAR BAZ
"
NIL
CL-USER> (close *stream*)
T
So, long story short: Try using finish-output on the stream before reading. If that doesn't work, check for command line options preventing buffering. If it still doesn't work, you could try wrapping the programm in some kind of pty-wrapper.

Resources