I don't know if I am clear in the title, but here me out. I have a package in which I bind *read-default-float-format*
to 'double-float
.
So far so good. When I load the library into fresh SBCL process in terminal, it works: I can read floats in double format. However, when I connect from Sly and switch to the package in repl, than I get an error:
The value
1.0
is not of type
DOUBLE-FLOAT
when binding INVISTRA::VALUE
[Condition of type TYPE-ERROR]
Is this because Slynk run in another thread?
I have tried, and would prefer to just let-bind over the function which needs to perform calculations with double precision, not for the entire process, but it is not a deal-breaker if I have to set it for the entire process. This is what I would like to achieve:
(in-package #:invistra)
#+sbcl
(declaim
(sb-ext:disable-package-locks *read-default-float-format*))
(defun ensure-symbol (name &optional (package *package*))
(intern (string name) package))
(defmacro define-interface ((client-var client-class &optional intrinsic) &body body)
(declare (ignore client-class))
(let* ((intrinsic-pkg (if intrinsic (find-package '#:common-lisp) *package*))
(format-func (ensure-symbol '#:format intrinsic-pkg))
(initialize-func (ensure-symbol '#:initialize-invistra))
(*read-default-float-format* 'double-float))
`(progn
(defun ,format-func (control-string &rest args)
(apply #'format ,client-var control-string args))
(defmacro ,(ensure-symbol '#:formatter intrinsic-pkg) (control-string)
(formatter ,client-var control-string))
(define-compiler-macro ,format-func (&whole form control-string &rest args)
(format-compiler-macro ,client-var form control-string args))
(defun ,initialize-func ()
,@body))))
That does not work at all :-), but Setf-ing the *read-default-float-format*
does. However, I have to set it again when I connect from Slynk.
What do I miss here, and how do I make it to work?
(For the info: it's a fork of Invistra which I have adapted to implement a similar format function; haven't changed any names yet).
As far as I know, special variables in all implementations that support native threads use thread-local storage to store local bindings of special variables in threads. You can change the global binding of special variables once during the start of your Lisp environment, for example by adding the following code in ~/.sbclrc
(if you are using SBCL):
(setf *read-default-float-format* 'double-float)
You should be able to have this result:
(type-of (read-from-string "0.1"))
=> DOUBLE-FLOAT
Alternatively, you have to configure Sly so that it knows how to bind special variables in threads. According to SLY User Manual - 5.10 - Multi-threading, adding the following code in ~/.slynk.lisp
should be enough:
(push '(*read-default-float-format* . double-float)
slynk:*default-worker-thread-bindings*)
More generally, when you create threads in Common Lisp, you are probably doing so using bordeaux-threads
, which accepts a list of bindings. Assuming we reset the special variable to 'single-float
, you can create a thread where the binding is modified as follows:
(bt:make-thread (lambda () (print *read-default-float-format*))
:initial-bindings '((*read-default-float-format* . 'double-float)))
#<SB-THREAD:THREAD tid=0 "Anonymous thread" RUNNING {10076B2CB3}>
DOUBLE-FLOAT
But even if you don't want to use this library, this is only matter of writing a function where you locally establish bindings. For example, sb-thread:make-thread
does not accept a list of initial bindings, but you can write your own wrapper:
(defun my-make-thread (function)
(sb-thread:make-thread
(lambda ()
(let ((*read-default-float-format* 'double-float))
(funcall function)))))