This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
So I'm usually bootstrapping projects that use deps.edn
. Today, I tried making one just from scratch and I'm confused how I get clojure to actually look at deps.edn
. It doesn't seem to recognize the file even though I'm running it in the same folder.
{:paths ["src/main"]
:deps {metosin/reitit {:mvn/version "0.5.18"}}
:dev {:extra-deps {djblue/portal {:mvn/version "0.34.2"}}}}
Thanks, fixed that. I only put the aliases in because my path wasn't working.
{:paths ["src/main"]}
Got a file in src/main/server.clj
and none of it seems to be read when I start clojureIn fact, even (+ 1 2) doesn't work in the file
(ns main.server)
(+ 1 2) => Syntax error
(defn do-something []
(+ 25 256))
(do-something)
In calva, I was evaluating this file and got
; Syntax error compiling at (src/main/server.clj:3:1).
; Unable to resolve symbol: + in this context
In regular clojure, I can do (+ 1 2)
but I none of the stuff in the namespace is definedTried with {:paths ["src"]}
, didn't work
And then not seeing it for some reason, and then when you switch the repl to that namespace, the namespace hasn't been setup to refer stuff from clojure.core
If you replaced + with clojure.core/+ it would work because a fully qualified name doesn't need the refers
Does deps not load it when it's on the path?
Paths just specifies where to look for code. require
is what loads a Clojure namespace.
If you're seeing +
not defined, you probably did in-ns
before you require
'd the ns.
In src/main/server.clj
, what is your ns
form?
(ns main.server)
OK, so :paths ["src"]
is definitely what you want instead of "src/main"
-- because namespace "paths" are relative to the classpath: src
is on the classpath, so src/main/server.clj
maps to main.server
relative to the classpath.
Alright fixed that. Still the same issue
Did you restart your REPL after updating deps.edn
?
Yes. So it sounds like there's a process that my bootstrapped apps do that I'm not doing here.
Also, how exactly are you starting your REPL and what exact steps are you taking in Calva when evaluating code?
There definitely seems to be something fairly fundamental you're missing...
I'm just doing jack in with deps.edn
and then evaluating the (+ 1 2)
in the file. In a regular project this would switch namespace and evaluate it
(perhaps if this project is up on GitHub, we can take a look and provide more specific feedback?)
Are you at least evaluating the ns
form in the file, before trying to evaluate other forms that follow it?
When I do that, I am able to evaluate (+ 1 2)
. I'm wondering why it's not just loading it automatically like say a luminus app does when it's launched. There doesn't seem to be anything in dpes
my other apps would be doing.
I can upload a repo
(I'll note that posting questions in #C03S1KBA2 tends to make people assume a lot of basic working knowledge with Clojure and its tooling -- posting those same questions in #C053AK3F9 will tend to encourage answers that make no assumptions about what you know and what you did)
I don't understand what you mean about "loading it automatically". Just starting a REPL doesn't load anything. Requiring namespaces is how code is loaded and compiled.
Yes, I had assumed before :paths
would do that, but it sounds like there's something in other project that is requiring all the namespaces automatically.
I suspect you're seeing behavior in your Luminus app that you think is "default" but in fact leans heavily on user.clj
being loaded and executed in that project -- and you just don't realize it.
Let me take a look
(I personally think user.clj
is "evil" and I see it trip a lot of people up -- I prefer explicit actions over implicit ones so I avoid user.clj
and encourage others to do the same and therefore learn the first principles without the "magic")
Luminus is a huge collection of libraries and configuration and it glosses over a lot of basic knowledge... I think it's a terrible way for beginners to learn about Clojure...
Despite this question, I have done a lot with my user.clj
and I'm pretty familiar with how it uses Integrant and other components, but I have never had to think about this particular thing. I see user.clj
requiring a lot, but I'm not seeing anything that, for instance, goes into the user
namespace and evaluates the file.
Or is it just because the default ns clojure opens is user?
Okay yes, that is it
When Clojure starts up, if user.clj
is on the classpath, it is loaded (I think that was a terrible idea), which means all the code in that file is executed.
But user.clj
should never be on your default classpath -- with deps.edn
, it should only ever be brought in via an alias (like :dev {:extra-paths ["dev"]}
so that dev/user.clj
is loaded).
But I still say it's a really idea.
Oh yes, in the case of this project, that's what's happening. It's only there for a dev alias.
So that explains why that works in this project
You can have a :dev {:extra-paths ["dev"]}
alias and have, say, repl.clj
in there and then open that file and eval it (or eval (require 'repl)
from another file).
You can jack-in and specify additional aliases to include in Calva so, yeah, if you didn't jack-in with :dev
then it wouldn't be loaded.
(again tho', if you use something other that user.clj
you can still open and eval the whole file -- even if it isn't on your classpath -- once you've jacked in)
And I'm assuming shadow-cljs watch
is also automatically evaluating function definitions, and that's why luminus projects can also read those files without evaluating the file first?
No idea. I don't use Shadow. And I don't use Luminus (see my comment above).
But if you don't know what the command does, ask specific questions in #C6N245JGG and #C077KDE3A etc to make sure you understand what "magic" is happening behind the scenes.
Sure. And to clarify, if I can evaluate files that aren't in the classpath, does the classpath do anything? is it just allowing other files to require it?
This is why I encourage beginners to always start from scratch and avoid these complex templates etc.
The classpath determines what code is available for the program being executed. The fact that you can evaluate additional code into a running REPL is orthogonal to that.
I mean I went in with Brave clojure before I ever touched luminus, like everyone recommended. But that doesn't mean I ever came across something that explained the classpath well. In fact, multiple things described it as being what loads code
Evaluating a file into a running REPL is the equivalent of evaluating each of its forms, one after the other, into the REPL. The classpath determines what is available when the program starts up -- but you can add new namespaces dynamically at runtime.
Well, the classpath doesn't load anything. It just tells Java (under the hood) where to look for classes and "resources" -- and that's what Clojure does when you require
a namespace: it says "What resource should that namespace be defined in and can I find it on the classpath?"
Alright I think I get it. Can require things in the classpath only, but nothings automatically loaded. if user is requiring another namespace, I am able to see defined functions in that namespace purely because user needed to be evaluated, so those namespaces needed to be as well.
Maybe this article I wrote five years ago might help? https://corfield.org/blog/2018/04/18/all-the-paths/
Thank you, I'll read that
@U042LKM3WCW the https://practical.li/clojure/ online book covers a lot of the Clojure CLI tool workflow, with lots of examples
How do you guys navigate your Lisp source code? I've been looking to get more into Paredit lately. I find that whenever I go anywhere, while it gives great precision, it requires so many commands. Take for example the two s-expressions in Clojure
(defn foo []
(println "foo"))
(defn bar []
(println "bar"))
Let's say my cursor is resting after the enclosing "
in "foo"
, and I want to get to the same place in the bar
function. In order to get there using paredit, I would have to do the following:
• 2x paredit-forward-up
to get out of the first s-exp (foo)
• 1x paredit-forward-down
to get into the next s-exp (bar)
• 3x paredit-forward
to get to the function body (`(println "bar")`)
• 1x paredit-forward-down
to get into the function body
• 2x paredit-forward
to get to after the "bar"
in the function body
That is 9 commands, just to get from one extremely small (and contrived) s-expression, to another extremely small s-expression. Usually, the expressions are WAY deeper, and requires a magnitude of more shortcuts and mental overhead to navigate (for me).
Is this even what you're supposed to do with Paredit, or is Paredit-navigation meant to be used together with arrow keys?
I have used vi-binds for most of my career, and have for a few years used Vim with Calva, but after getting fed up with the friction of keyboard shortcuts, I have tried to stick to only Paredit while working with Clojure, since structural editing is important for lisps.
Are the gains perhaps mostly in the structural editing, not in the structural navigation? How do you guys navigate?What's the friction you're talking about between Vim and Paredit?
I've been using them both (or rather, IdeaVim instead of Vim) for years just fine.
I use Vim where it makes more sense and Paredit in other places.
Like in the case above, I'd just use jjjj
(`4j` if I'm feeling like typing in numbers).
The Vim's Easymotion extension takes things further where I can go to "that character x
over there half a screen away" in 4-5 key strokes.
I don't exclusively use paredit to move around. But, I've got up/down/left/right set to Ctrl+up, Ctrl+down, Ctrl+left, Ctrl+right. So it's not really 9 commands, I hold Ctrl and than it's 9 movement . In the above case, I would just move the cursor down four lines normally. Or in Emacs, you've got avy jump too where you type the letter to jump too. Like jump to "b and it goes there directly.
@U2FRKM4TW I'd probably get there much faster in vim as well; I have just heard so much about structural editing and navigation after I started meddling with elisp and clojure, but if this is the extent of structural navigation (I.e. you need to combine structural navigation with "dumb" character-based navigation to be effective) then the premise is a bit flawed, in my opinion. It's still good and precise, but I guess I will have to pick Vim-binds back up again. As for the friction, the vim integration in vscode hijacks ctrl-w in a way I haven't been able to disable, and comes with a boatload of shortcuts that activate on certain editor events, making customizing it to let the Calva-keybinds just work a hot mess @U0K064KQV given that you're both experienced clojurists and both combine character-based navigation with structural navigation, I'll settle on that for now. Speaking of emacs, do you have any recommendation on a starter point for a clojure environment? I have used Doom the most, but abandoned it after the upgrade-process broke on me the nth time.
No tool is perfect, you have to pick what best suits your needs. I would also suggest looking at the NeoVim extension for VSCode - no clue about Ctrl+W but IME it was easier to configure and use overall.
Structural editing is really useful on top of normal movement. But I've never tried to exclusively navigate around using it. I don't use vim bindings, because I dislike modes. But also most vim navigation is line based, which isn't great for Lisp, and where structural navigation is what you'll want instead. For example, going up a line isn't how you get to insert a statement between two existing one. In a line based lang it would work, but instead in Lisp you need to go up a form and then between them.
I use spacemacs personally, in holy mode (non vim). And have some additional custom shortcuts of my own. But the downside of Emacs is very much that it often breaks, and you need to fix things yourself, etc. The upside is, you can relatively easily learn to debug and fix things yourself.
@U2FRKM4TW I tried the neovim plugin again a few weeks ago, and it had some other issues, but I will give it some fiddling, gotta make this setup work now, hm. @U0K064KQV I briefly tried Spacemacs before Doom, but it seemed even more complicated (and thus harder to debug) than Doom to me, but will give it another shot at some point. > For example, going up a line isn't how you get to insert a statement between two existing one. In a line based lang it would work, but instead in Lisp you need to go up a form and then between them. This, and the tendency to just "chop text" in vim normal-mode, completely bypassing the paredit rules that uphold the structural integrity, is what kind of led me down the Paredit route. I'll try to find some middleway, thanks for the good feedback!
In my view structural editing is to manage changes to the code without breaking the structure of the code. Changes include creating, transposing and deleting expressions
Navigation features are secondary to me as there are so many other ways to navigate within Emacs + Evil or Neovim.
In the original post example, i'd probably use search, or jump if there were multiple "bar" in the current file.
Before I was comfortable with multi-modal editing I used Smartparens and Spacemacs lisp state for structural editing. I added evil-cleverparens to ensure evil commands in normal mode did not break the structure.
As confidence and experience grew with multi-modal editing I found some of the simpler structural editing can be done using common multi-modal editing key bindings. I mostly navigate with vim-style normal key bindings like %
to jump to a matching paren
Some other examples at https://practical.li/spacemacs/spacemacs-basics/vim-style/vim-tips-for-developers/
I have been using parinfer recently with Neovim and find it a simpler approach especially with multi-modal editing, assuming you trust parinfer to do the right thing and understand the basic operation
I don't use Paredit for moves like that. Nothing wrong with moving up/down/left/right manually, or just as likely ace-jump-mode. My emacs config is http://github.com/jackrusher/dotemacs (with the supplied user-specific config)
I'm also using Parinfer with Neovim, and it's pretty good. No need for extra commands.
I'm not sure about other editors/environments, but in Cursive you can have parinfer and paredit on at the same time (and of course IdeaVIM), so you can use paredit commands or just dd
-delete whole lines and let parinfer take care of the parens, depending on what feels more efficient or intuitive in each case.
...and I do a lot of short-distance jumping to whatever I'm looking at through forward and backward search (mapped to Space+j/k
) in VIM.
Or if you want to go extra crazy with this, you can buy a Tobii eye tracker and get the https://talonvoice.com/ software to make your cursor jump to whatever you're looking at (I actually bought one a while back, but haven't managed to get around to setting it up yet, plus I'm not sure how well it works on a larger screen).
What I also use very often to quickly jump out of a deep form is beginning-of-defun
/`end-of-defun` which are bound to C-M-a
/`C-M-e` in my setup (not sure if default or custom)
> Are the gains perhaps mostly in the structural editing, not in the structural navigation? Short answer: yes I think so. Or at least this is how I found a balance after various experiments. My Emacs/Evil setup is hybrid (meaning: a mess 😉) but in general: • jump around/navigation mostly vim-ish, some plain Emacs, almost nothing Lisp specific (I use Cleverparens&Paredit); • structural edit, hell yes Cleverparens&Paredit 🙂 but also room for Vim&Emacs tricks. But this is me, to each their own discovery of their own favourite setup. Happy experimentation 🙂
In emacs I would type C-s bar to get to bar, then C-b to jump back a word
And then of course structural editing takes over when I need it, as folks here are saying
Personally I use evil and so I'd just
/"bar<RET>
You could also use avy-goto-char-2
and type "b
and it'd jump right there
avy is a very good package for jumping around windows and buffers to things you can see
If you did avy-goto-char
then "
it'd highlight every double quote on your screen and mark them with a letter you can use to jump to the particular one. So in this case it'd be c
unless you had more double quotes that sort earlier among your windows
I'm partitioning by a boolean, and want true to always be the first element, and false the second.
Ya, already switched to group-by, just wasn't sure if it could also be done with partition
you could alternatively drop-while false - that's going to drop elements from the seq, but presumably that's what you want if you want the first partition to always be true
depends on the details of what you're doing, but this might be useful https://www.juxt.pro/blog/new-medley-partition-fns/