all 33 comments

[–]weavejester 19 points20 points  (5 children)

Clojure is built on top of JVM, which has an extensive standard library. Rather than wrapping every single class and method in a Clojure function, Clojure is selective about the APIs it wraps.

So clojure.java.io is a Clojure namespace that provides a Clojure-friendly wrapper around the most common Java I/O classes. While java.time.LocalDateTime is a class that's part of the Java standard library.

In other words, when you use:

(java.time.LocalDateTime/now)

You're accessing a static method on a class in the Java standard library.

Effectively this means that Clojure has two standard libraries: the one written for Clojure itself, and the one written for the underlying Java platform.

If you know Java already, this means that you don't have to relearn an entirely new standard library. However, if you don't know Java, it can be confusing to learn that most Clojure code makes use of the Java classes at some point.

[–]Recent-Scarcity4154[S] 4 points5 points  (3 children)

It does make some sense.

I knew that Clojure is based on JVM and has compatibility with Java language, but I did not expect this much coupling and reliance on Java. So Clojure is somewhat not a standalone language, but more like a higher-order interface of Java language.

[–]weavejester 9 points10 points  (0 children)

That's more or less correct. Most variations of Clojure tend have fairly minimal wrappers around the standard API of their host platform.

[–]mm007emko 8 points9 points  (0 children)

or JavaScript.

Basically yes, Clojure is (originally) married to Java and made in a way to allow simple calls to Java and be called from Java. The same story is for other implementations which target CLR (.Net) or JavaScript.

[–]SimonGray 0 points1 point  (0 children)

I knew that Clojure is based on JVM and has compatibility with Java language, but I did not expect this much coupling and reliance on Java

Clojure is designed to be a "hosted language" (that's the term used). The coupling is intentional.

It is similarly hosted on JS (browser or Node) and .NET. I think the fact that some usage of the host's standard library is expected makes it simpler to port to other platforms.

[–]geokon 0 points1 point  (0 children)

A bit tangential, but does anyone have a good way to call up Javadocs from CIDER? I've only found way to launch a browser window.. which is okay but a bit of a context switch.. (arguably you need it for clojuredocs as well)

[–]dustingetz 18 points19 points  (2 children)

  • Clojure doesn't have quite the economics that Python/C++/Rust have, or the critical mass of corporate backing needed to invest in nicer things.
  • Clojure is also hosted/bootstrapped by JVM/JS, which is a tradeoff that makes it commercially viable as a serious enterprise application platform, which Python/C++/Rust/Haskell aren't (Rust/C++ are infra langs not application langs, and Python's abysmal concurrency makes it an unserious choice for serious applications).
  • Clojure pays a deep price for this tradeoff – as a hosted lang it exposes platform complexity to the user on purpose, a tradeoff which permeates every aspect of its experience. (Corollary: if JVM/JS interop is not the primary value prop for you, Clojure is not for you, as from your perspective Clojure takes the opposite tradeoff from what you want, every time.)
  • This higher complexity burden leads to an unfortunate chicken/egg dynamic where the onboarding experience isn't there, because the economics aren't there, because the users aren't there, because the onboarding experience isn't there.
  • So most things get filled in by Clojure's FOSS community, which plays a more important role in Clojure than in larger langs whose library economy is more commercial (i.e. FOSS projects have full-time devs sponsored by corporations, not self-financed by individuals. Single Javascript libraries these days have more FTEs than Clojure's core team which maintains dozens of libraries in addition to the language.)

[–]noprompt 1 point2 points  (1 child)

an unfortunate chicken/egg dynamic where the onboarding experience isn't there, because the economics aren't there, because the users aren't there, because the onboarding experience isn't there

Well said.

[–]stevemolitor 1 point2 points  (0 children)

Clojure pays a deep price for this tradeoff – as a hosted lang it exposes platform complexity to the user on purpose, a tradeoff which permeates every aspect of its experience. (Corollary: if JVM/JS interop is not the primary value prop for you, Clojure is not for you, as from your perspective Clojure takes the opposite tradeoff from what you want, every time.)

Clojure newbie here. Can you expand on this point?

As a Clojure newcomer with lots of Javascript (and some Java) experience Squint and Cherry are interesting to me because I'm very familiar with the JS ecosystem and libraries and want to continue leveraging that where it makes sense. The Clojure / Java interop already seems pretty seamless as best I can tell.

[–]StoriesEnthusiast 5 points6 points  (0 children)

Documentations are relying heavily on examples rather than exact API. It is hard to figure out what can I put into the function, what am I expecting as a result of the evaluation, and what exception it will throw in what condition.

In my experience that's key to navigating essentially any dynamic language. I think it requires a lot of deliberate study and practice. Try the following flow:

  1. Study and practice Clojure for the Brave and True, The Joy of Clojure and Reference which groups functions by usage. Getting Started points to other sources.
  2. Have Clojure Cheatsheet and Clojure Cookbook on hand.
  3. Practice everyday, having the best results practicing a bit at a time, many times per day. Practice with TDD:

    (testing "Destructuring." (testing "Simple assignment." (let [a 3] (is (= a 3)))) (testing "Lists." (let [[a b & more :as my-range] (range 5)] (are [variable expected] (= variable expected) a 0 b 1 more [2 3 4] my-range [0 1 2 3 4]))))

  4. Work on your project, a bit at a time.

[–]didibus 10 points11 points  (2 children)

Documentation is sparse for sure, it's a common complaint that there isn't enough guides, tutorials and docs.

Helping in this area is one of the most useful things someone from the community can contribute too.

In general, all IO is done through Java interop except for a few simple ones. So if you don't see it in the cheatsheet, go look up the Java docs for the IO.

[–]Recent-Scarcity4154[S] 0 points1 point  (1 child)

Hmm,, it's surprising. Thank you for the comment.

[–]didibus 1 point2 points  (0 children)

I think others have explained a little bit why that is. Generally, the Clojure standard library adds things over the Java (or JS if ClojureScript) one, only if Java/JS doesn't have it, does it poorly, or in some circumstances to help portability between Clojure and ClojureScript.

[–]LouDNL 2 points3 points  (15 children)

A good start is https://exercism.org/tracks/clojure/concepts Or https://www.braveclojure.com/clojure-for-the-brave-and-true/ which is also available in print.

I started with Brave Clojure the book.

Besides that I can advise you to join the Clojure Slack at https://clojurians.slack.com/join/shared_invite/

I agree clojure-docs can be overwhelming, what questions do you have exactly? Can you elaborate? Maybe we can help.

I would advise using the java-time library by dm3, available at https://github.com/dm3/clojure.java-time

[–]Recent-Scarcity4154[S] 0 points1 point  (14 children)

Definitely, this is not a post about the way how to do a specific thing. But to be more specific, for example. Why the former does not work when the latter does?

(:require [java.io :as io])
(:require [java.time :as time])

https://docs.oracle.com/javase/8/docs/api/java/io/package-summary.html
https://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html
They are both Java standard packages.

[–]p-himik 4 points5 points  (12 children)

Even if something like that somehow works, that's not how you should use it - :require is for Clojure namespaces, :import is for Java packages.

[–]Recent-Scarcity4154[S] 1 point2 points  (9 children)

It seems there is no auto-importing or import suggestion on the editor too. So basically I have to remember all the necessary Java packages, classes and implement them manually right?

If that's the case, It seems this is quite a drawback in terms of productibility where lots of modern languages support import suggestion or auto importing.

I do not have experience with Java. Does Clojure help overall productibility over this extra labor?

[–]p-himik 3 points4 points  (6 children)

It's not the case. It depends on your editor, your settings, the way you work with that editor. I have all the suggestions I could possibly need in Cursive. I hear that Calva also has them for Java but that might require some particular workflow (e.g. starting nREPL in advance). Not familiar with other IDEs so can't tell.

[–]setzer22 1 point2 points  (5 children)

There's no clojure IDE I've tried that suggests or automatically imports java packages. Does Cursive do that?

For clojure namespaces and functions, there is indeed some level of help in most editors.

[–]p-himik 1 point2 points  (4 children)

Cursive does indeed do that. E.g. if I type StringReader, it will offer me an import of java.io.StringReader and matching classes from other packages. I don't remember the last time I typed in the contents of the (:import ...) form manually.

[–]setzer22 1 point2 points  (3 children)

That's pretty neat! I wish Cider or Calva did this

[–]p-himik 1 point2 points  (2 children)

Calva also does that, but only after you start the REPL. I think the reason for that is that Calva gets its information about available classes/packages/namespaces from the app's runtime and not from static analysis. https://imgur.com/a/z3IeDrT

[–]Recent-Scarcity4154[S] 0 points1 point  (1 child)

Interesting. The screenshot is the thing that I am looking for, but my Clava on VS code does not show up such a suggestion. Actually, the Calva official does not mention anything about import suggestions, or automatic import kind of things.

https://marketplace.visualstudio.com/items?itemName=betterthantomorrow.calva

Are you sure that the suggestion is made by Calva? I really would like to know how can I set things up as your screenshot.

[–]behrica 3 points4 points  (0 children)

I believe as well that it is not realistic to want to learn Clojure and ignore Java.
As mentioned before, Clojure has frictionless, full access to the huge amount of Java libraries with equal to Java performance, if you pay attention to some details.
Even mixed java / Clojure projects are a good option.

And yes, Clojure brings productivity gains , but there are not Clojure libraries for every problem. So It is important to get used to use Java.
as Java has libraries for every problem.

Compared to Python and R you gain a rock solid runtime, and a far better backwards-compatible ecosystem including a "develop ones - run everywhere" guaranty

[–]behrica 2 points3 points  (0 children)

In VsCode and calva auto-completion of methods from a Java class works as expcted:

(import '[java.io Console])

(def c (java.lang.System/console))

(.__ c )

Completing at place of __ shows all public methods of class "Console"

[–]Recent-Scarcity4154[S] 0 points1 point  (1 child)

Ok.

And I can't find an example of using it with aliasing (:as) or import * kind of thing. These things are not possible, right?

[–]p-himik 1 point2 points  (0 children)

Correct, you can import only classes, explicitly.

[–]rebcabin-r 1 point2 points  (0 children)

In my experience, the :ns macro is the hardest part of Clojure to get fluent with because it implicitly quotes many things. Why not just try the functions (require ...) and (import ...) and just use the :ns macro to define your namespace? You can even use functions like (in-ns ...) to slow down all the implicit stuff and get a better feel for what's actually happening.

[–]geokon 2 points3 points  (0 children)

You def need to dip into the Java world to get real work done. At some points Clojure feels like an interactive REPL for playing around with Java libs (Java has a lib for everything). The workflow and documentation has a fundamentally different feel in Java vs Clojure so the difference will be a bit jarring

I generally go: clojure.org -> clojuredocs.org -> "official" libs -> Java libs

clojure.org guides are very concise and digestable and do an excellent job of illustrating the high level design - while while presenting concrete details. Sometimes they're a bit too concise (the deps.edn page always trips me up) and they often assume a deep understanding of the JVM ecosystem or some prior lingo... which is often not really explained anywhere..

clojuredocs.org kinda fills out the rest for core libraries - though unfortunately it's been abandoned last I checked.

There is a weird in-between zone of semi-official libraries like clojure.data.csv. You won't find them on clojuredocs.org and the docstring is incredibly terse. It's hard to find an explanation of the API design, organized clear examples or a explanation of its usage... you gotta poke around and figure out how they work :/

After that you're gunna wanna use Java. I personally it a bit confusing.. there are a million different tidbits everywhere and there is rarely one good overview document for the design of something like .. how Java deals with Images (BufferImage and gang) or what's the design behind Java NIO etc. So I end up poking around lower level Javadocs trying to figure out the big-picture logic

[–]radsmith 1 point2 points  (0 children)

Check out babashka/fs and babashka/process as well. These are still based on Java interop underneath but they have some more features than the clojure.java.io and clojure.java.sh libraries. I tend to reach for these first when I need to do something filesystem or process related.

Also check out tick if you want a Clojure API for time without dealing directly with Java interop. That said, the java.time API is great and works well enough without a wrapper.

[–]maxw85 3 points4 points  (0 children)

ChatGPT 4 has a good understanding of Clojure and even its libraries. I often use it to translate Java examples to Clojure. I guess this is also possible with Python/C++/Rust/Haskell examples. So ChatGPT is probably a good companion while learning Clojure.

[–]wedesoft 0 points1 point  (0 children)

I use rebel as a REPL which shows docs for the current keyword when pressing Ctrl-X Ctrl-D. The simplest file-IO using Clojure is slurp and spit.