Fork me on GitHub
#clojure
<
2020-12-30
>
pez11:12:43

Help a tool smith, please. What is a good workflow for navigating namespaces that a tool like Calva could support? Calva encourages working from “inside” the source code files themselves, but many people are still using the repl window as well. Observing how people try to navigate namespaces most of them dive straight down the “How things can go wrong” path mentioned here: https://clojure.org/guides/repl/navigating_namespaces#_how_things_can_go_wrong Experimenting a bit with it I find that I can probably add a command to Calva to load the current namespace even though it has been created (by evaluating the code in the file). That would “encourage” this “wrong” path though, I think… Maybe that doesn’t matter as long as it gets the work done?

vemv11:12:06

I think it's quite common to have the IDE able to run a specific "reload" command (like tools.namespace's refresh, a given app's custom reset, etc) under a keyboard shortcut So, as a IDE user I just hit the keyboard shortcut whenever my code is ready. That doesn't preclude using the repl, or even evaluating a form from the file (that's a favorite of mine; as it's a hybrid approach) When I visit a new file that wasn't required before, my emacs does just (in-ns 'that-ns) but does not attempt to automagically load its code That's my responsibility, be it via the keyboard-bound refresh, or various repl/eval interactions

vemv11:12:45

That would seem a sensible approach to me as it's minimalistic/transparent and doesn't impose any specific workflow

p-himik11:12:51

Cursive can "load file in REPL", whatever that means. Interestingly, it seems to also load all the dependencies:

;; hgs/xxx.clj
(ns )

(def a 1)

;; hgs/yyy.clj
(ns hgs.yyy
  (:require []))

(println "Hello" )

;; REPL
(ns )
=> nil
(require ')
=> nil
a 
Syntax error compiling at (/tmp/form-init16785379888913250927.clj:1:350).
Unable to resolve symbol: a in this context
;; I executed the "Load file in REPL" command on hgs/yyy.clj here
Loading src/hgs/xxx.clj... done
Loading src/hgs/yyy.clj... 
Hello 1
Loaded

pez11:12:56

Thanks. Sounds like I can go ahead with my idea for having a command that loads the current namespace then, from both your answers. In my spike it looks quite exactly like that Cursive workflow.

👍 3
pez11:12:35

Like so.

👍 3
pez11:12:30

At line 27 there I issued the command. If that is not clear from the screenshot.

p-himik11:12:48

Will it also load all the dependencies in the same way?

pez11:12:38

It evaluates all the code in the file, so its requires will get loaded. If that is what you mean.

p-himik12:12:31

Not really - in my example above, the dependency is also evaluated, not just loaded. If I understand it correctly, otherwise evaluating yyy.clj would not create because the namespace already exists.

vemv12:12:00

what happens if both namespaces define a? That specific feature seems more delicate

pez12:12:42

I think that will work as in your example. Let me try…

p-himik12:12:24

@U45T93RA6 It would be ns1/a and ns2/a - no issue here. The REPL has only one namespace as currently active.

👀 3
pez12:12:19

With file contents similar to your example @U2FRKM4TW

pez12:12:00

Note that the load file in repl command already exists in Calva. So loading the pez.yyy namespace can be done from the file. What’s new in my spike here is that I can do it from the repl prompt. Not sure if that is what the Cursive example showed.

p-himik12:12:55

It's not the same. Note that in my example I execute what would be (ns ) in your case, not (ns pez.yyy).

pez12:12:47

Hmmm, that actually exposes an issue in current Calva. If I do (ns ') before loading pez.yyy I run into the problem I posted in the thread start. I guess that is quite uncommon in practice, but anyway.

p-himik12:12:31

A probably more common workflow that would suffer from the same issue: 1. Evaluate xxx.clj in REPL 2. Make some changes in xxx.clj 3. Start using those changes in yyy.clj (so yyy depends on xxx) 4. Load yyy.clj in REPL 5. Receive an error

p-himik12:12:47

Also, I have no idea if Calva handles it but in Cursive I regularly suffer from the situation described here: https://nelsonmorris.net/2015/05/18/reloaded-protocol-and-no-implementation-of-method.html Is it possible for Calva to automatically reload relevant namespaces to avoid that error?

p-himik12:12:43

I know that it's not really about the initial topic but it seems quite relevant given that it's also about loading files and their dependencies.

pez12:12:21

I filed an issue on Calva now, please ensure that I have understood the problem correctly and let me know if I haven’t: https://github.com/BetterThanTomorrow/calva/issues/907

p-himik12:12:52

LGTM, thanks!

pez12:12:10

What happens in step 3 in the workflow you describe there?

p-himik12:12:27

By "start using" I meant just writing new code.

pez12:12:09

So that should work in Calva w/o error, but now I am a bit more on the defence here so must try before confirming. 😃

p-himik12:12:32

Please do tell how it turns out. :)

pez12:12:59

You were right with your step 5 unfortunately . 😃

p-himik12:12:20

At least the reason is known, so this confirmation just increases the priority, I guess.

pez12:12:21

But in this case I can reload and things start to work. I think I do this more or less on auto-pilot.

p-himik12:12:00

But it would also work in the original workflow outlined in that issue #907.

pez12:12:14

I can probably get out of the mess in the reported issue by reloading … yeah, you beat me to it.

😄 3
pez12:12:01

So, fixing #907 should fix this too, I think. And fixing #907 would mean to help the user with loading the files in order. I wonder if the current command Calva has for refreshing namespaces actually does that… must check.

pez12:12:13

Nope. Hmmm…

pez12:12:22

Now to your other question there. I’m pretty sure, given #907, that this will be the same problem in Calva. Will try it now to see for myself.

👍 3
pez13:12:47

So, Calva doesn’t offer any help with this currently. How would you like it to be supported?

p-himik13:12:39

No idea as of now - I started evaluating Calva just a couple of days ago (IntelliJ IDEA pisses me off more and more with each release). I guess it should be as non-intrusive as possible. I.e. in the article that I linked, z.core should be reloaded automatically as well. But the user has to be notified about it in an obvious way, ideally with a reason describing why z.core was reloaded at all.

pez13:12:39

I am reluctant to make Calva load things automatically. It would certainly “help” with many common support questions we get, but I really think the user must be in charge of what is being loaded, and when.

p-himik13:12:43

Without doing it automatically, how would you fix 907?

pez13:12:56

But a command for reloading all files that depend on a given namespace.

p-himik13:12:52

With such a command, would it be possible for a user to make it automatic via configuration?

pez13:12:25

I hear you about 907, but I see it as that the command is named Load Current File and Dependencies it sort of promises something it doesn’t keep.

pez13:12:18

So, a setting where Calva always loads dependent namespaces on Save, you mean?

pez13:12:53

I don’t think I can do it on mere re-evaluation…

p-himik13:12:51

> reloading all files that depend on a given namespace It wouldn't be necessary. I think only the files that use the protocols and other similar entities would need to be reloaded. > it sort of promises something it doesn’t keep Not sure I follow. If you do load everything, what is the promise that's not kept? > So, a setting where Calva always loads dependent namespaces on Save, you mean? It may be a setting. Ideally, it would be something that's scriptable. Sorry, I have no idea yet whether it's at all doable but AFAIK you can write CLJS code to extend Calva and/or VSCode (a huge advantage compared to IDEA+Cursive).

pez13:12:34

About 907. Given that the dependency is not loaded by the command, (if the namespace has been created), I see that as a bug.

pez13:12:24

If we can target the reload to protocols, and similar, then I might be persuaded to make it an automatic thing, but I haven’t thought it through yet.

pez13:12:58

My question regarding Save was that that would be enough automatic. The way my workflow is leaves me quite often with a lot of unsaved files, because I mostly just evaluate my programs to existence. 😃

pez13:12:33

There is currently a setting where Calva evaluates the file on Save. So if we introduce a command that dependent files be re-evaluated, then the auto-setting for that would be in the same spirit.

p-himik13:12:04

Ah, I think I misunderstood one thing - I wouldn't want for anything to happen automatically on Save. I also prefer to issue the command to reload stuff manually. By "automating" I meant automatically reloading every file that needs to be reloaded to keep the app working when a user manually reloads one file.

pez14:12:57

Ah, then we're on the same page. Once a command behaves in a predictable manner I'm all for automatic. 😎

🤝 3
didibus19:12:12

What Cursive does is, if you reload file X, and it depends on file Y, and you changed file Y but did not reload file Y, when you reload file X, Cursive will also reload file Y

didibus19:12:38

Which avoids a lot of the "what can go wrong"

didibus19:12:33

Also, I think it's great that you can run the command from the repl as well, so it's more like reload namespace

didibus19:12:23

Cider also has something similar: cider-ns-refresh will reload all modified files

p-himik19:12:52

It's not only about the modified files. It's also about namespaces that have been created but were not populated properly. Although as pez said, this use case is probably quite rare.

didibus19:12:27

What do you mean by not populated properly?

didibus19:12:46

That you forgot to load the rest of the namespace code? Or that loading threw an exception?

p-himik19:12:58

I meant executing (ns xxx) in a REPL, like described in examples above.

didibus19:12:01

Oh, so you switch the reply namespace manually by executing (ns xxx) and then try to call a function in that namespace, but it doesn't exist because you actually never loaded the corresponding file?

p-himik19:12:45

No, it's not about one namespace. Have you checked the examples above?

didibus19:12:35

Ok, I was looking at the prior example, I see now.

didibus19:12:26

So you mean, it be better to have it load all dependencies, not just modified ones?

p-himik19:12:10

I think so. Although I'm not sure how that would work in a workflow with global state atoms or something like that.

didibus19:12:02

State is always the downsides to these "auto-sync" like features.

p-himik19:12:30

Right. So should be fine as long as users know what they're doing and the consequences of their actions.

didibus19:12:14

Ya, but changing namespace in the repl is pretty rare no?

didibus19:12:48

Also, I do think Cursive has both Load and Load All no?, And the latter would reload all

p-himik19:12:36

Above, we have agreed on this, yes. My "although" above is about the whole "reload files that were not directly reloaded by the user" thing. Regardless of the reasons for their reloading. I don't see "Load All" in the list of REPL actions in Cursive.

didibus19:12:34

Hum, I might have been mistaken. Haven't used Cursive in a while

didibus19:12:47

So in Cider, you have: refresh and refresh all, which run the tools.namespace refresh and refresh-all. Both of those should work in the example given. You also have reload and reload-all. Those are based on require :reload and require :reload-all respectively. I think both of those would also work.

didibus19:12:56

And finally you have Load buffer, which is just like sending all the code in the buffer to be evaled. That one would not work, since it won't force reload a lib that is already loaded.

pez00:12:58

I know this is a big ask, @didibus, but I get a bit dizzy by trying to sort all this out into a coherent suggestion on what Calva could do in this space. Knowing how you can structure thoughts… If you do find some time, I’d appreciate a feature request (or several) on the Calva repo. I also of course understand if you do not have the time.

pez00:12:25

(The load current namespace command is already in the works, https://github.com/BetterThanTomorrow/calva/pull/904 It is the rest of this that I find myself thinking in circles with.)

didibus19:12:51

I was wondering, tools.namespace also unloads, but what if it didn't? Wouldn't that solve most of the caveats it has, at the expense of you could miss the fact one function calls something that no longer exists.

noisesmith19:12:36

@didibus you'd need some kind of manual override if you changed a defmulti though, for example

didibus19:12:36

True, I could get in the habit of having a (def my-multi nil) above them

didibus19:12:06

But I'm also wondering here, maybe tools.namespace could be smarter and unmap defmultis but not the rest?

didibus19:12:47

I also wouldn't be too bothered if when I changed a defmulti I still needed to manually deal with it

didibus19:12:07

Are there other cases like this? Apart from defmulti?

kingcode21:12:08

I am trying to use memoize on a recursive fn, but the recursive calls don’t go through memoize...is there a way to do this?

(defn is-even? [x]                                     (println :X x)                               (condp = x
 0 true
-1 false
(is-even? (- x 2))))
(def ev? (memoize is-even?))
(ev? 4)
;;=>
;; :X 4
;; :X 2
;; :X 0
;; true
(ev? 4)
;;=> true
(ev? 6)
;;=>
;; :X 6
;; :X 4
;; :X 2
;; :X 0
;; true

Reily Siegel21:12:51

You could write

(def a-fn (memoize (fn [x]
                    (println x))))

kingcode23:12:21

Whoops sorry, I misspoke. My problem is memoizing no responding to recursive calls - only the outer call. Any idea?

kingcode23:12:11

Sorry, I figured it out…thx

Reily Siegel21:12:55

Is it possible to force Clojure to resolve import statements at compile time? Currently import statements produce bytecode that looks like

class_ = ((Namespace)RT.CURRENT_NS.deref()).importClass(RT.classForNameNonLoading((String)"a.java.class"));
I need to be able to run the resulting AOT'd clojure code through a java remapper (obfuscator), which does not seem to work when the imports are resolved in this way. Upon further inspection, this only happens in clojure_ns$loading___xxxx___auto__xxx.class. In all other instances, the import statement is resolved correctly and is properly converted by the remapper.