File refactoring guide?

In my first clojure project, I’ve reached the point where I want to refactor my single .clj file into several. Is there a guide of how to do this so that it works with cider, leiningen, and the clojure compiler? For example, do I need to have a declaration of the same namespace in every file with a transitive chain of :require declarations? Or does each file need its own name space, I hope not. Or like Common Lisp, do I need one namespace declaration (ns ...) and an (in-ns ..) atop all the other files which contribute that name space? And if so how do I tell the compiler which order the files should be loaded/compiled?

Once that work is done, how do I load the project when I start emacs/cider?

By the way, I suspected this might be documented in the leiningen tutorial, but I didn’t find it there. So I filed an issue suggesting that this be documented. I’m not 100% certain that the leiningen documentation is the best place to describe this flow, but as a beginner it is the first place I thought to look.

Each file has its own namespace because the namespace matches the path to the file: src/foo/bar.clj will start with (ns foo.bar ,,,) and each ns form will have the :require entries for code that it directly uses (the compiler deals with transitive dependencies).

This does seem to be an area that is poorly documented. I think there’s an inherent assumption that people are just used to packages and files and naming from other languages but it’s not universal.

Looking at open source examples might help you, e.g., https://github.com/seancorfield/usermanager-example so you can see a typical project structure and how ns and :require are used within it.

1 Like

it is a bit surprising that each file needs its own namespace. Where I’m coming from file names, directory names, namespace names, and system names are all independent from each other. A “system” is a set of files in and other systems, which are considered as a single block for loading, compiling, etc.

Is moving function between files (refactoring) therefore really a lot of work?

If you’re using Emacs you can lean a bit on clj-refactor to help with that

If you have a function f in namespace foo.bar (hence in src/foo/bar.clj) and you moved it to quux.wibble (in src/quux/wibble.clj) then you will have to adjust all of the calls sites, yes.

Let’s supposed you called it from app.main (in src/app/main.clj) so you would have:

(ns app.main
  (:require [foo.bar :as bar]))
,,,
(bar/f :some :args)
,,,

You move it to the new namespace so now app.main may need to require that:

(ns app.main
  (:require [foo.bar :as bar] ; assume you're still calling other bar/* functions
            [quux.wibble :as wibble]))
,,,
(wibble/f :some :args)
,,,

You can break out a namespace over multiple files, but it’s not recommended and will break most tooling, including possibly lein and Cider (though I haven’t tried it, but my guess is they won’t work as well)

So I don’t recommend, but yes, what you’d do is in your primary file have a top level (ns ...) form which defines all configuration for the namespace.

Now in your other files at the top you’d have a (in-ns ...) for that same namespace.

And again in your main file (or it could be anywhere but gets really confusing otherwise), at the end of the file, you can just use calls to (load "other_file") to define the order you need them loaded in.

Hi Sean, do I really need the :as bar and :as wibble? Can I just require them and use the names without qualification?

Does a namespace in clojure designate which of its symbols are exported and which are private? or does the name space which requires it make that decision?

@bsless, What do I need to do to get these clojure-emacs functions into my emacs session? Do I need to clone the repo myself? or are they part of melpa ?

I found the instructions here.
loading clj-refactor

Yes, you can require namespaces without aliasing with :as, and then use the entire namespace. For instance (ns foo.bar (:require [foo.baz])) then later in foo.bar refer to foo.baz/quux.

You can control which vars are available in other namespaces by using defn- or setting ^:private metadata:

(defn ^:private my-fn ...) 

(FYI that’s shorthand for ^{:private true}.)

If you ask, “Why does defn- exist but not def-?” the answer is here in the FAQ.

I was just lazy and installed spacemacs. Got everything out of the box.

Whoa… it hurts my brain to imagine multiple namespaces defined in one file. What languages do it differently, and what are the benefits? Sounds confusing…

You can also refer them all, but that’s generally a bad practice, because it becomes really hard to figure out where a var resides:

(:require [foo :refer :all])

When at the REPL and you want to quickly require all the functions and minimize the amount to type just to try things out though, the above comes on handy, there’s even a more handy variant:

(:use foo)

As Dave says, you can require the namespace without an alias and then use the whole name in the qualified symbols – but it’s not common and I’d say it’s considered non-idiomatic.

Also, as Dave notes, you can use defn- to declare a function private and ^:private on a def. But private functions and Vars can still be accessed in other namespaces: (#'foo/f :some :args) will still work if f is a private function, as will @#'wibble/v for a private var.

Clojure isn’t the only language where “private” is… advisory… but it’s unusual and therefore something to bear in mind when you’re considering whether to make something private. Often, instead of private, what you’ll see in projects is a separation of API and implementation into two namespaces. The latter commonly has .impl in its name.

3 Likes

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.