Closed hovsater closed 3 years ago
No, it's not really possible. A single "eval top block" can issue one to three commands to the REPL, so the value of these vars are not bound to last evaluation. Also, there's no *e
on most REPLs - most exceptions are captured and serialized for Chlorine to capture...
I'll probably add an entry on the FAQ regarding these vars :)
@mauricioszabo I see, that makes sense. 🙂 So am I right in assuming Chlorine does one-off evaluations to the Socket REPL instead of having an ongoing connection to it and that's why it's really not technically possible to achieve that?
For instance, doing echo '(+ 1 1) (println "The value is" *1)' | nc 0 5555
seems to print the correct output:
$ echo '(+ 1 1) (println "The value is" *1)' | nc 0 5555
user=> 2
The value is 2
nil
but I'm not familiar with how Chlorine actually send the blocks when you evaluate them, so perhaps, this isn't feasible.
No, it does have an ongoing connection. The problem is that when you evaluate anything, Chlorine tries to detect:
ns
form?If 1
is true, it'll send a command to set the filename (binding *1
to it);
If 2
is true, it'll send a command to set the namespace (again, binding *1
)
Then, it'll send the eval command (once again, binding *1
).
Now, if 1
and 2
are true (which they are, most of the time), everytime you send an evaluation you'll have *1
and *2
bound to nil
and the current namespace (that's what the REPL returns when you set a filename and namespace). This means that "original *1
" (the one that contains the evaluation result) is now *3
.
You can check this yourself by trying to eval something, then evaluating *3
. You'll see that it indeed contains the result of last eval; and that *1
probably contains nil
, and *2
contains an instance to clojure.lang.Namespace
BTW, a simple correction: Chlorine will try to detect the ns
FIRST then the filename. The reason is that when it detects the filename it also sets the right row, so if we send the in-ns
command first it'll mess the current row
I see. That makes sense. So, it "works" but not in the way you would expect, making it less useful? I tried out the example and it worked just like you said it would. However, the example below didn't set *1
, *2
and *3
to anything, which made me a bit confused again.
I assume *1
, *2
and *3
are set to nil
due to what you described earlier, but given I have no filename and no namespace, what commands are being run before the actual block I'm trying to evaluate?
EDIT:
Nvm, I just realised that evaluating the actual vars will obviously change the value of them again 🤦 My bad! This makes sense. Using the vars will be highly indeterministic, which make them less useful.
Do you have a ns
form on this editor? This could explain why *2
is bound to nothing.
As for the nil
in *3
, it's probably because you eval'ed *1
first (then making it nil
). Also, you don't have a filename but Chlorine will still try to set the current row, binding it to nil
The current code that does handle evals in Chlorine starts here: https://github.com/mauricioszabo/repl-tooling/blob/master/src/repl_tooling/repl_client/clojure.cljs#L95-L96
and goes to here: https://github.com/mauricioszabo/repl-tooling/blob/master/src/repl_tooling/repl_client/clojure.cljs#L40-L48
Thanks for taking the time to explain this so throughly. It's really appreciated. 🙂
Sorry for bringing this up again but when I woke up this morning I realised I didn't fully understand what you meant yesterday.
Given that you said
If 1 is true, it'll send a command to set the filename (binding 1 to it); If 2 is true, it'll send a command to set the namespace (again, binding 1) Then, it'll send the eval command (once again, binding *1).
So *1
will always point to last expression evaluated.
Now, if 1 and 2 are true (which they are, most of the time), everytime you send an evaluation you'll have 1 and 2 bound to nil and the current namespace (that's what the REPL returns when you set a filename and namespace). This means that "original 1" (the one that contains the evaluation result) is now 3.
So how come *1
isn't the value of whatever was being evaluated? Are there expressions being evaluated after our own code has been evaluated? My understanding was that our own expression was the last thing being evaluated.
So how come *1 isn't the value of whatever was being evaluated
It is - but the problem is that when you evaluate something in Chlorine, it'll send other commands before your eval. So it'll be re-bind to the result of previous command - for example, the "set filename and/or row"
@mauricioszabo perhaps I misunderstand how the REPL bind the dynamic variables, but anything before my eval shouldn't be a problem since *1
is bound to the last result. I'm sorry if I'm asking a lot of questions, just trying to wrap my head around this.
Ok, so the thing is: if you don't have a ns
form on your editor, Chlorine will try to run a code to set the current row. Let's suppose this code is called (set-row <some-number>)
. So, if you're on the line 20 of the editor, this is what will be run:
; Evaling (+ 1 2)
(set-row 19) ; Binds *1 to nil
(do
(+ 1 2)); Binds *2 to nil, and *1 to 3
Now, on the second evaluation, if you eval *1
:
(set-row 19) ; Binds *1 to nil, *2 to 3, and *3 to nil
(do
*1) ; *1 is nil, so it'll bind *1 to nil, *2 to nil, and *3 to 3
set-row
is called with 19 because of the do
form
@mauricioszabo thanks a lot, I finally get it! 🎉 Really appreciate you taking the time to explain this in detail. 🙂
When you evaluate a block, it would be nice to be able to use
*1
,*2
and*3
and*e
respectively to reference return values and the last exception.Taken from the Clojure docs:
It seems to be supported by most REPLs.
I'd be happy to contribute a small amount to make this happen if you're interested in pursuing it. 🙂