clojure 2026-04-03

I’m experimenting in exporting cl-format to a Scala program. I have it basically working (with the help of chatgtp). but I’ve discovered something about cl-format. in Common Lisp the A directive to format triggers the print-object method. I was hoping the A directive in clojure’s cl-format would do the analogous thing and use java .toString , but alas it doesn’t. If I try to print a Scala List using the clojure/cl-format, it prints something like this.

#object[scala.collection.immutable.$colon$colon 0x4dd94a58 List(1, 2, 3, 400)]
Here is what chatGPT says is happening, and this is surprising to me. It is surprising that clojure’s printer doesn’t default to calling toString() on java object which it otherwise does not know how to print. ⚠️ Why toString isn’t used In Clojure: ~A → uses pr-style printing (via print-method) • It does not always call .toString() directly Instead it goes through Clojure’s printer, which: has special handling for Clojure types • falls back to #object[...] for unknown ones

I think you might be able to just override this for any unknown value by calling (defmethod clojure.core/print-method :default [v] (.toString v)) - but this will of course have highly non-local effects and may cause inconsistencies if you're relying on other parts of Clojure's printing behavior.

can I do this from scala?

or do I need to somehow create a jar file which I load from scala in every project where I want the behavior?

I've never tried to call Clojure code from Scala, so I don't really know how you might override multimethods at runtime in that context!

You can do anything from Java that you can do in Clojure - there's a Java API. Using that Java API would be the most correct approach, since it's a public API. But it can be cumbersome. An alternative is to call (.addMethod clojure.core/print-method dispatch-val (fn [v] (.toString v))). That's exactly what defmethod does under the hood.

did you try pr and pr-str by chance? They should print something that's readable, but as persistent clojure data, not Scala types.

sorry, missed some of what you did already, but here's something that might be easier to follow if you want to try it

(require '[scala.jdk.javaapi CollectionConverters])

(-> (CollectionConverters/asScala '(1 2 3 4))
    (CollectionConverters/asJava)
    (pr-str)) ;; => (1 2 3 4)
This constructs a persistent list wrapped in a java.util.List implementation. You give that to pr-str and it should do what you want. You can give asJava any scala collection.

@p-himik yes, but the thing I need to then figure out is how to do that from Scala. Remember, I’m not programming in Java, but in Scala here.

IIRC converting Java to Scala is even easier than converting Clojure to Java in this case. If you need the type of print-method, it's clojure.lang.MultiFn.

For this simple case, perhaps it suffices just to call load-string … this saves having to figure out how to create fn [...] from scala

Probably (x: Object) => x.toString(). Maybe just _.toString or _.toString() will also work, I don't remember Scala.

any way chatgpt gives a simple recipe. easy to try. However, chatGPT warns that the method suggested above is wrong.

of course I can experiment to verify. Here’s what it says.

⚠️ Your Clojure form is not valid This:

(defmethod clojure.core/print-method :default [v] (.toString v))
won’t work because: • print-method dispatches on class, not keywords • :default is not a valid dispatch value here So even if you successfully call load-string, it won’t behave the way you expect. What you probably want instead If your goal is: > “Fallback to .toString() for unknown objects” You typically define a method for Object:
(defmethod print-method Object [v ^java.io.Writer w]
  (.write w (.toString v)))

to me this sounds wrong (havn’t verified) because certainly clojure has already defiend print-method for Object . no?

ChatGPT is wrong.

✔️ 1
😂 1

again it’s all speculation, until I try it to see what happens.

> print-method dispatches on class, not keywords The :default is a special keyword mentioned in the docs of defmulti.

✔️ 1
👨🏻‍🎓 1

But it does give an easy recipe for evaluating clojure code from the Scala side. at least simple clojure code. Calling load-string from Scala Now, to your actual question — yes, it’s straightforward. Scala code:

import clojure.java.api.Clojure
import clojure.lang.IFn

object ClojureEval {

  private val loadString: IFn =
    Clojure.`var`("clojure.core", "load-string")

  def eval(code: String): AnyRef = {
    loadString.invoke(code)
  }
}
🚀 Usage
ClojureEval.eval(
  "(defmethod print-method Object [v ^java.io.Writer w] (.write w (.toString v)))"
)
That’s it — the code is evaluated inside the Clojure runtime.

Apart from that, print-method doesn't dispatch on just the class, it also respects the :type metadata. No clue whether it's important in your case.

Yes, load-string retrieved via the public API and used for all Clojure code is probably the way to go. Especially if you need more than one form.

👍🏻 1

OK, this seems pretty interesting so far. Here are the results. Here is the Scala setup code:

import clojure.java.api.Clojure
import clojure.lang.{IFn, RT}


object ClojureEval {

  private val loadString: IFn =
    Clojure.`var`("clojure.core", "load-string")

  def apply(code: String): AnyRef = {
    loadString.invoke(code)
  }
}


object CLFormat {

  private val require: IFn =
    Clojure.`var`("clojure.core", "require")

  require.invoke(Clojure.read("clojure.pprint"))

  private val clFormatFn: IFn =
    Clojure.`var`("clojure.pprint", "cl-format")

  def apply(fmt: String, args: Any*): String = {
    val allArgs = (null +: fmt +: args).map(_.asInstanceOf[AnyRef])
    val seq = RT.seq(allArgs.toArray)
    clFormatFn.applyTo(seq).asInstanceOf[String]
  }
  
  ClojureEval(
    "(defmethod print-method Object [v ^java.io.Writer w] (.write w (.toString v)))"
    )

That’s very reasonable. Here is the call to cl-format (in Scala CLFormat). I only have the CLFormat version which returns a string, so I still have to call scala’s println

def main(argv:Array[String]):Unit = {
    println(CLFormat("Hello ~A!", "world"))

    println(CLFormat("""~&~
                      expecting t1 <: (or t1 t2)~@
                      t1=~A~@
                      t2=~A~@
                      depth=~A~@
                      reps=~A""",
                     100, 200, 300, List(1,2,3,400)))
  }

And here’s what is printed:

Hello world!
expecting t1 <: (or t1 t2)
t1=100
t2=200
depth=300
reps=List(1, 2, 3, 400)

🎉 1

😀😀😀😀

I’ve ran into an interesting problem with Cloverage, again. I have a .ssl.TrustManagerFactory and I am trying to pass nil Keystore to init function. This works normally:

(let [^KeyStore ks nil
       tmf (doto (TrustManagerFactory/getInstance (TrustManagerFactory/getDefaultAlgorithm))
              (.init ks))])
works fine and selects the correct overload. But cloverage wraps all forms into something and it will cause overloads like this to lose metadata so the actual overload will be selected by Clojure’s reflection utility at runtime. Problem is, the value is nil, so clojure cannot determine which overload to call and so picks one of the overloads, unfortunately the wrong one. Has anyone had something like this?

we see the noisy output from cloverage throwing reflection warnings all over because of this, but don’t run into it selecting the wrong method. You might try the new clojure method selection syntax?

> When you supply :param-tags metadata on a qualified method, the metadata must allow the compiler to resolve it to a single method at compile time.

https://docs.oracle.com/javase/8/docs/api/javax/net/ssl/TrustManagerFactory.html so there are two single arg init methods. So you could type hint with the correct version you intend, and hopefully cloverage preserves it

not sure how to do this

i think like this:

(let [^KeyStore ks nil
      tmf (doto (TrustManagerFactory/getInstance (TrustManagerFactory/getDefaultAlgorithm))
            (^[java.security.KeyStore] TrustManagerFactory/.init ks))]
  tmf)
#object[javax.net.ssl.TrustManagerFactory
        "0x15171dfc"
        "javax.net.ssl.TrustManagerFactory@15171dfc"]

you specify the method TrustManagerFactory/.init and annotate with the argument vector of the method you want

to select the other one you would use ^[ManagerFactoryParameters]

awesome that fixed it

💪 1

anytime! glad to help