Fork me on GitHub

Is there a way to provide a docstring to a clojure function defined in terms of def instead of defn ?


I have some functions that could benefit from the point-free style but I don't see a way to add metadata or a docstring


def takes an optional doc string argument, and metadata can be provided on the symbol itself


(ins)user=> (def ^{:foo [1 2 3]} f "takes no args and returns nil" (fn f []))
(ins)user=> (doc f)
  takes no args and returns nil
(ins)user=> (meta #'f)
{:foo [1 2 3], :line 1, :column 1, :file "NO_SOURCE_PATH", :doc "takes no args and returns nil", :name f, :ns #object[clojure.lang.Namespace 0x6f012914 "user"]}


@luishendrix92 you can type (doc def) and see more

Jim Newton09:11:41

How can I load code created by another JVM language? Does it suffice to ask the other language to produce a .jar file? Can I load the .jar file explicitly in clojure? Or is it more complicated than that? E.g., does it have to be installed into some mutually agreed directory with a bunch of boiler plating surrounding it? I'd prefer to have something light which I can later just delete with no lasting side effects.


clojure usually (unless you encode how and what to compile using system tools) have no idea how to compile another PL so to use libs written for JVM but in another language (eg. scala) it should be provided as compiled jar there are some options to load jar file, all depends on how you run your application: 1. if you run using java command — add this jar to classpath (`-cp` option) 2. if you running application using clojure cli (aka. tools.deps) — you can add dependency pointing to that jar (`{:deps {some/id {:local/root "/path/to/file.jar"}}}`). in that case jar file can be placed anywhere 3. you can “install” that jar into local maven repository (usually $HOME/.m2/repository/…)

Jim Newton09:11:57

Currently i'm running clojure in two different ways. from the lein command line either via "lein test" or "lein repl", and also running from cider within emacs. At the moment, I'm not interested in installing applications based on .jars coming from different JVM langauges, rather I'm just interested in using the clojure reflection (`clojure.reflect/type-reflect`) capabilities to examine classes which were created elsewhere. elsewhere being directly from java or from Scala.

Jim Newton09:11:12

So in these cases would it suffice just to persuade java or Scala to create a single .jar file? For example. I'd like to use clojure.reflect to examine the classes produced by the following scala code.

trait A {
  def foo(s: String): AnyRef

trait B {
  def foo(s: String): String

class Foo extends A with B {
  override def foo(s: String): String = s


I can’t say much about scala workflow, specifically about the content of produced jar file. but it should be sufficient if at the end this jar file will have a bunch of .class files which can be loaded in clojure for analysis

Jim Newton09:11:26

so is the .jar a sort of tar file of .class files? Can I put the .jar in /tmp/erase-me-tomorrow/file.jar and then somehow tell lein to load it so that the classes are available at the clojure repl??

Jim Newton09:11:33

or is it the case that the .jar file might actually be any of many many different things depending on the language which produced it?


JAR (or JavaARchive) is just a package file format. there are no restrictions to file types it contains. Typically it contains many java .class files optionally together with source file used to produce those .class’es so in general .jar file might contain anything depending on PL


for instance — I saw jar files produced by javascript with resources meant to be used in java application


jdk also providing a tool to examine content of jar files jar tf /path/to/file.jar


(answering on the question about leiningen) if I’m not mistaken — to add local jar via leiningen you should use :resource-paths ["/path/to/local.jar" …]


path can be absolute or relative to project’s root

Daniel Östling09:11:30

I’m sure this is answered already; when I add more require statements in for example core.clj, and in project.clj, what steps do I have to take in Emacs/Cider environment to get the editor pick that up? cider-restart doesn’t seem to be enough, I have to shut down the whole cider/nrepl process it seems.

Daniel Östling09:11:54

Oh, I’ve done lein deps to download the deps as well.


running lein repl or start repl from emacs should be enough but will require restart of the repl in case you add dependencies in project.clj

Daniel Östling09:11:57

Yeah okay. Thanks 🙂

Jana Chaloupková10:11:23

Hello! :3 Would someone be willing to help me out with an exercise I'm doing? It's a transcription of DNA to RNA and this is what I came up with:

(ns rna-transcription)

(defn- valid? [dna]
  (nil? (re-find  #"[^CGTA]" dna)))

(defn to-rna [dna]
  (let [modify-letters (map {\G "C" \C "G" \T "A" \A "U"} dna)]
    (assert (valid? dna))
    (apply str modify-letters)))
However, I was asked for further edits/optimalisation to avoid nil? (done), to do validation based on count (done) and (what I'm struggling with) to exchange map for keep (filters nil automatically and I don't iterate over the string again).
(ns rna-transcription)

(defn- valid? [dna, rna]
  (= (count dna) (count rna)) true)

(defn to-rna [dna]
  (let [rna (keep #{\G "C", \C "G", \T "A", \A "U"} dna)]
    (assert (valid? dna rna))))
I don't understand how to use keep instead of mapping for transcription though. :/ Thank you in advance!


just change #{\G "C", \C "G", \T "A", \A "U"} to {\G \C, \C \G, \T \A, \A \U} a map is a function of key in clojure but #{ is a set literal -

Jana Chaloupková10:11:42

Thank you! I was getting a bit desperate and trying out everything. 😅 This still fails tests though.

lein test :only rna-transcription-test/it-transcribes-all-nucleotides

FAIL in (it-transcribes-all-nucleotides) (rna_transcription_test.clj:18)
expected: (= "UGCACCAGAAUU" (rna-transcription/to-rna "ACGTGGTCTTAA"))
  actual: (not (= "UGCACCAGAAUU" nil))

lein test :only rna-transcription-test/transcribes-cytosine-to-guanine

FAIL in (transcribes-cytosine-to-guanine) (rna_transcription_test.clj:6)
expected: (= "G" (rna-transcription/to-rna "C"))
  actual: (not (= "G" nil))


right, you have (assert … expression inside of let this is not really clear from the docstring but assert returns nil in case underlying expression resulting to trythy value


(defn to-rna [dna]
  (let [rna (keep {\G \C, \C \G, \T \A, \A \U} dna)]
    (or (assert (valid? dna rna)) rna)))


there is one of a few “valid” changes to do what you want


or you can be a little bit more explicit and use if here

(if (valid? dna rna) rna (throw (ex-info "Invalid DNA sequence" {}))

❤️ 3
Jana Chaloupková10:11:15

Works like a charm, thank you!


keep is a nice approach, thanks. I just used a map with an anonymous function to wrap the or around the hash-map, which is used as a function over each character in the dna string. If the character is not found, then throw the error.

(defn to-rna
    (map #(or (dna-nucleotide->rna-nucleotide %)
              (throw (AssertionError. "Unknown nucleotide")))
         dna )))
with the hash-map to define the transformation as a simple dictionary
(def dna-nucleotide->rna-nucleotide
  "DNA to RNA transcription mappings"
  {\G \C
   \C \G
   \T \A
   \A \U})

❤️ 3

anyone know if cider has a way to specify a fn to run on jack in ? I see a lot of people creating startup shutdown reload fn's so I am guessing no but it would be nice to jack in and have the startup fn executed instead of typing it each time

Jim Newton12:11:56

is my understanding correct that I cannot use {:pre ... :post ....} within a defmethod?


You can, place it after arglist definition (defmethod foo :dispatch-value [& args] {:pre pre-fn :post post-fn} …)


user=> (defmulti adder :k)
user=> (defmethod adder :y [{:keys [y]}] {:post [(pos? %)]} (inc y))
#object[clojure.lang.MultiFn 0x5286c33a "clojure.lang.MultiFn@5286c33a"]
user=> (adder {:k :y :y 1})
user=> (adder {:k :y :y -2})
Execution error (AssertionError) at user/eval140$fn (REPL:1).
Assert failed: (pos? %)


yes, more precisely — have a look at how defmethod is implemented (fn & fn-tail) so fn-tail can have everything that can take fn like a function name to produce better error stack traces or extra metadata etc.


oh that makes sense just call it in the file, so if i create user.clj and make that the namespace and call the startup there at the top level, I was over complicating it in my head 🙂


@oliver.marks there are cider specific variables for restarting the repl via cider which builds upon that approach


awesome thanks for the links @jr0cket


I was aware of integrant never realised it had hooks into cider that will be super handy and gives me a reason to use that library now 🙂


I havent had chance to create an integrant example, but it should work just like mount (with slightly different libraries)

Dave Russell12:11:32

Is there a way to give a set of keywords the same spec, without having to specify each one like this:

(s/def ::foo fancy-spec)
(s/def ::bar fancy-spec)
(s/def ::baz fancy-spec)

Alex Miller (Clojure team)12:11:58

You can s/def one keyword to another

Dave Russell17:11:02

Thanks! Is it possible to do it in bulk? Say I have 20 keywords that I all want s/def'd to the same spec, and don't want to repeat myself for 20 lines 🙂

Alex Miller (Clojure team)17:11:11

not part of spec, but macros exist :)

Dave Russell17:11:16

Understood -- thanks!


I'm working through The Little Schemer in Clojure and have arrived at the last chapter. It uses data types from Scheme for the examples and I'm not sure how to approach it from Clojure. Anyone happen to have done it before? They define two functions that rely on Scheme's *const, *quote, *identifier, *lambda, *cond, and *application and then build upon that in later problems:

(define atom-to-action
 (lambda (e)
   ((number? e) *const)
   ((eq? e #t) *const)
   ((eq? e #f)  *const)
   ((eq? e (quote cons)) *const)
   ((eq? e (quote car)) *const)
   ((eq? e (quote cdr)) *const)
   ((eq? e (quote null?)) *const)
   ((eq? e (quote eq?)) *const)
   ((eq? e (quote atom?)) *const)
   ((eq? e (quote zero?)) *const)
   ((eq? e (quote add1)) *const)
   ((eq?   e (quote sub1)) *const)
   ((eq? e (quote number?)) *const)
   (else *identifier))))

(define list-to-action
 (lambda (e)
   ((atom? (car e))
     ((eq? (car e) (quote quote))
     ((eq?   (car e) (quote lambda})
     ((eq? (car e) (quote cond))
     (else *application)))
   (else *application)))) page 181


How can I create a project with Clojure (Clj) with some template mechanism to have the directory structure like that produced by lein new app but using deps.edn? I tried the following:

clojure -M -m myname.sandbox
with @seancorfield’s clj-new aliases, but the directory structure is sandbox/src/myname/sandbox.clj, but I need it to be sandbox/src/sandbox/core.clj I wonder if I could just use lein new app sandbox and add deps.edn by manually translating project.clj to deps.edn, I wonder if it would work to use clj for execution and continuous tests. I tried the following. It did run with project.clj:
clojure -M -m sandbox.core

Alex Miller (Clojure team)17:11:47

I doubt I can answer your question, but I'm missing what "doesn't work" means - error? different structure than expected? if so, what structure?


@alexmiller yes, it's different structure. I've improved my question. Thanks!


@hiredman Thanks for the pointer. With minor variation, the following achieves the structure that I want:

clj -X:new-app :name sandbox.core


@yubrshen foo.core is a bit of an anti-pattern -- the only reason .core is prevalent in Clojure is because Leiningen added it to single-segment names as a default to avoid ending up with single-segment namespaces (and therefore top-level class names in JVM byte code).

👍 3

@seancorfield thanks for the recommendation. I'll follow your advice in general. I'm following a live coding session. In which the old convention was used, I may have to follow that for the particular case.


That's why clj-new strongly encourages you to pick a proper name for your project -- either <orgname>/<myproject> or <username>/<myproject> -- and the Clojure CLI also discourages libraries with the same group and artifact ID (i.e., use a proper orgname or username in both your project and your top-level namespace segment).

🙏 3

Even when building an application, following that "proper" naming pattern makes it less likely your own namespaces will conflict with any third party library code you use.


(the author of Leiningen is on record as regretting his decision to add .core by default and says he should have required multi-segment names -- and lein new will already reject a bunch of names so it's not like it isn't already opinionated 🙂 )


@seancorfield thanks for the recommendation. I'll follow your advice in general. I'm following a live coding session. In which the old convention was used, I may have to follow that for the particular case.


@yubrshen Unless a Leiningen project relies on certain plugins that inject code or config, then it should be just a matter of manually adding the right :paths and :deps details are in the deps.edn file to run a project Leiningen project with Clojure CLI tools instead. I do this for and can just use {:paths ["src"]} as the only dependency is clojure and that is pulled in from the install configuration. You can always refactor the namepaces and file names once you have an understanding of the tutorial you are following.

Old account20:11:02

Hi, how to get :as all in the second argument?

(fn [db [_ new-filter-kw]]
    (assoc db :showing new-filter-kw))


Do you mean you want a parameter named all to be equal to the value of the entire 2nd argument, which will be a sequential data structure of some kind (e.g. list, vector, etc.) ?


If so, here is a sample REPL session showing one way:

user=> (defn foo [db [_ new-filter-kw :as x]] [db new-filter-kw x])
user=> (foo 1 [2 3])
[1 3 [2 3]]

Old account20:11:14

thanks, for some reason I was putting :as all outside of the argument array.

Rafael Gil21:11:01

Hi everyone! Not sure if I should post this here or on the clojurescript channel but here it goes: I'm starting learning clojurescript and I'm setting up my development environment. My dev environment is a remote box running Spacemacs, so my idea is to run the clojurescript repl in that box connected to a browser in another machine. I've tried the default "browser" repl without success (even changing the "host" to my IP instead of "localhost") I've also tried figwheel (using the "flappybird" example) but the browser tries to connect to a websocket on "localhost" again. So my question is: how do I change the host on figwheel to my host instead of always pointing to localhost? Thanks!


seems like a #figwheel-main question. assuming its not covered in the docs

Rafael Gil09:11:02

Thank you for the reply. I tried that but didn't work 😞 I tried :connect-url, :server-ip, :ring-server-options {:host} But none of those seem to do anything. I'm addind those to the project.clj file

Rafael Gil11:11:45

I'll post it in the right channel.