I’m a beginner lisper and I’ve decided to write a Telegram bot as a test project. Here I have faced some unexpected behaviour of let
. The code is in a form of .asd system with one file containing the following code. The important part of it to pay attention to:
-
long-poll-updates
function is basically a loop. -
A call to
read-updates
inlong-poll-updates
function -
A call to
check-integrity
inread-updates
function. -
checks
variable in let form.
On each iteration of the main loop I do (prin1 checks)
and see something that I struggle to look up or understand. On the first iteration of the loop it prints exactly the value that I’ve assigned to it. But each new iteration does not reassign nil
values back, but uses the same exact values left behind by the previous iteration’s operations.
(defun long-poll-updates ()
"Main loop. Repeatedly sends requests to get updates."
; Sets a variable for storing the last processed updates's ID.
(let ((offset 0)) (loop
(let* ((api-answer (get-updates-request offset))
(parsed-plist (jonathan:parse
(flexi-streams:octets-to-string api-answer))))
;; Read response and modify offset parameter to get next updates.
(let ((response-data (read-updates parsed-plist)))
(when (getf response-data :has-results)
(setf offset
(1+ (getf response-data :last-update-id)))))))))
(defun read-updates (response-plist)
"Reads the incoming long poll response:
checks for response validity/errors,
proceeds to an appropriate action."
(let ((response-data (check-integrity response-plist)))
(cond ((getf response-data :has-ok)
(cond ((getf response-data :is-ok)
;; Evaluate updates on successful poll
(cond
((getf response-data :has-results)
(setf (getf response-data :last-update-id)
(eval-updates response-plist)))
(t
(log-data "No results received."))))
(t (log-errors response-plist))))
(t (log-data "Received malformed JSON-response while long polling.")))
response-data))
(defun check-integrity (response-plist)
"Runs checks for valid JSON received, success/faliure and presence of new updates.
Returns a plist of checks passed/failed."
(let ((checks '(:has-ok nil
:is-ok nil
:has-results nil)))
(prin1 checks)
(loop :for (indicator value) on response-plist by #'cddr
;; If successful response:
:when (eql indicator :|ok|)
:do (progn
(setf (getf checks :has-ok) t)
(when (eql value t)
(setf (getf checks :is-ok) t)))
;; If any results:
:when (and (eql indicator :|result|)
(listp value)
(< 0 (length value)))
:do (setf (getf checks :has-results) t))
checks))
I suppose I expected function’s code to evaluate anew each time it is called, without realizing it even. Yet it seems that compiler evaluates data to some entity that never leaves memory, before the code is ever run. Could anyone point my mind at a greater idea that I’m dealing with here?
I see that lisp’s way is really “theory first” and I rarely find people actually documenting issues they face when approaching it in a blocky and litteral manner (or whatever it looks like when you’re new to lisp, but had prior coding experience). I find it incredibly strange even, but that’s not the point.
Alteration Dream is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.