2

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).

Edit:

My personal solution, after the info from @coredump is to start SBCL with --eval flag and tell it which double format to use:

sbcl --noinform --eval "(setq *read-default-float-format* 'double-float)"

Seems to work well for my use case.

asked May 19 at 5:59
5
  • I'm not confident enough to post an actual answer but I think special variables are thread local in most CL implementations, so you'd have to set it per thread. Maybe they inherit values from the parent thread so if you set it when the program starts it'll carry over? Commented May 19 at 10:24
  • @Shawn Yes they are thread local in SBCL, but I don't know if there is some mechanism that creates a copy of per thread or how that works. I also don't know how should some "foreign" thread like Slynk know which bindings it has to sett, and more importantly, I don't understand why I can't use them as a closure. I couldn't find anything online that lets me understand what is going on, so I asked. Commented May 19 at 11:27
  • sb-ext:disable-package-locks looks like a code smell to me, is it necessary? I don't see why initialize-invistra should ever be added to the Common Lisp package Commented May 19 at 11:51
  • 1
    You are correct, does not seem to be needed. I just tried. About initialize-invistra, don't take me for the word, but as I understand the code, they use this intrinsic/extrinsic macro tryckeri to make the interface either be a part of the package where it is used, or to live in it's own (external) package. That is how I interpret that idiom, and I see they use it in almost all of theirs libraries (those on s-expressionstas). Commented May 19 at 18:14
  • 1
    @coredump I surely misunderstood your question, sorry. Invistra is an implementation of CL format function. They use it as format implementation in SICL, so that is why they want it become part of CL package. I have re-used (and refactored) Invistra just as a little framework, to do something else, and don't use it that way. I just use it as external package for my needs. I think all, or almost all their libraries, as found on S-expressionistas GH, are used in SICL, and thus they use that little idiom intrinsic/extrinsic. Commented May 21 at 11:46

1 Answer 1

4

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)))))
answered May 19 at 11:45
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you very much. I had suspicions it is because of being in another thread, but I am not familiar enough with this, so I didn't know how to deal with it. If I don't want to affect all of the programs and images, I guess I should not put it into .sblrc file. Thank you very much for the answer, it is very helpful.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.