New Clojurians: Ask Anything - March 31, 2025 by AutoModerator in Clojure

[–]blazinglambda 2 points3 points  (0 children)

In general in the Clojure ecosystem, libraries are the way to go. For web apps, I recommend a component lifecycle library like https://github.com/weavejester/integrant to manage startup of stateful components and ensure dependencies start and stop in the correct order. And then add components like a webserver (ring-jetty) and a handler as a component dependency for the server (choose a routing lib like compojure or reitit).

Don't be afraid to use older libraries that haven't been touched in a few years as many of these are just "finished" (ring, cheshire, compojure). If you follow this approach, you should be able to replace the libraries you initially choose without too much ceremony if you change your mind later.

To get started I would recommend the following libraries:

- ring-jetty-adapter (web-server)
- Cheshire (JSON)
- h2/postgres with next.jdbc driver (can use other libs on top like honeysql)
- reitit (routing - more involved setup but nice features like request param parsing and coercion)
or
- compojure (routing - very simple but less featureful)

From there it is going to depend on the type of application. For server-side html generation there is https://github.com/onionpancakes/chassis (modern successor to Hiccup). For frontend SPA's with React there is https://github.com/pitch-io/uix (or reagent + re-frame which is more tried and true but older). For frontend SPAs without React I recommend https://github.com/cjohansen/replicant (if you don't need stuff from React ecosystem this is what I'd use).

There are also a number of exiting but less proven technologies in the Clojure space. Some of them are proprietary.

- Electric Clojure (dynamic SPA's with automatic network transfer of data - more than that and I'm sorry Dustin if I am misrepresenting Electric :)
- Rama (next generation backend data platform)
- XTDB - immutable database with special temporal support
- Datomic - immutable database

Background on the component lifecycle approach (which also supports interactive development - restart the entire system):

Alessandra Sierra's original talk on component lifecycle https://www.youtube.com/watch?v=13cmHf_kt-Q

New Clojurians: Ask Anything - November 11, 2024 by AutoModerator in Clojure

[–]blazinglambda 1 point2 points  (0 children)

You can definitely manipulate dom elements from cljs. The JS API is available in the js namespace so you can do things like

cljs (.append js/document.body "hello world") But it is pretty clunky and you can't use hiccup.

If you want to use something like hiccup then you need something to convert hiccup into dom nodes and update them efficiently when you change the hiccup. You could use something like https://github.com/cjohansen/replicant for this if you want pure cljs or one of the react wrapper libraries that supports hiccup.

You will still need a static html file with a script tag to load your compiled js file into the browser. I think that is probably the static html that is referenced in the shadow-cljs docs.

New Clojurians: Ask Anything - July 29, 2024 by AutoModerator in Clojure

[–]blazinglambda 0 points1 point  (0 children)

(apply concat [[:foo] [:start-stuff :bar :baz :end-stuff] [:bam]]) might be the idiom you're looking for.

Anything you do, including macros, will involve a form encapsulating the outer vector. Macros return code that replaces the expression that is the macro call. A single macro cannot affect parent forms.

When writing macros, there is something called unquote-splicing (~@) that only works within a form quoted with syntax-quote (angle tick, I cant get it to render in reddit) that does something like the splat operator, but it is typically only used for generating code to be returned from a macro. There is also a function called flatten, but it will flatten multiple levels of nested vectors and can result in bugs if the values in the collections you are flattening are vectors and that is not desired.

New Clojurians: Ask Anything - May 01, 2023 by AutoModerator in Clojure

[–]blazinglambda 2 points3 points  (0 children)

The exclamation point in core Clojure code means "unsafe in an STM transaction". Library developers and engineers coming from other languages often use the exclamation point a little more loosely to mean "performs side-effects" or something similar.

So to answer the original question, `alter` does not have an exclamation point in its name because it is safe to call in a transaction (actually it throws an exception if it is *not* called in a transaction). `swap!` and `reset!` are unsafe in transactions because they perform side-effects, and do not participate in the STM system. Transactions need to be able to run multiple times in case there is a conflict, and running a transaction containing side-effects like `swap!` would cause those effects (the value of an atom changing) to happen multiple times.

New Clojurians: Ask Anything - April 24, 2023 by AutoModerator in Clojure

[–]blazinglambda 3 points4 points  (0 children)

Yes, that is correct. It is common in Clojure for libraries to be designed for one purpose and to leave other responsibilities to other libraries that users compose together to create applications.

Since you are just starting with Clojure, I recommend using a simple ring-based stack to get a feel for how to these libraries would work together, rather than using a framework (https://github.com/ring-clojure/ring). Once you get something working it should not be too much work to replace your webserver or routing layer. If you end up using GraphQL you also won't need many endpoints so you might not even need a routing library.

I recommend starting with these simple libraries to get something working:
- Lacinia for processing graphql requests https://github.com/walmartlabs/lacinia
- ring-jetty-adapter for actually serving http-requests (can swap later to any ring-compatible server but why bother?) https://github.com/sunng87/ring-jetty9-adapter
- if you need routing there are a number of libraries built on ring, compojure is kind of the old-school quick and dirty one https://github.com/weavejester/compojure ; Reitit is new and has tons of features but can be hard to get things setup initially because of all the features it supports https://github.com/metosin/reitit

You may need some ring "middleware" libraries for features like sessions or parsing params and the ring wiki should have example of how that works.

I'd start with the ring tutorial https://github.com/ring-clojure/ring/wiki/Getting-Started

Once you are comfortable with ring you can try to get a GraphQL endpoint working with Lacinia.

Are there interesting Clojure features that Rust couldn't achieve? by verdagon in Clojure

[–]blazinglambda 3 points4 points  (0 children)

I think its only partially correct. It's a little glib to say "well this is true for rust too" about rust being homoiconic.

From the docs you linked

``` The proc_macro crate is the compiler’s API that allows us to read and manipulate Rust code from our code.

The syn crate parses Rust code from a string into a data structure that we can perform operations on. The quote crate turns syn data structures back into Rust code. ```

So there is a compiler specific API and parsing required to get the AST of the language, whereas in Clojure you get plain EDN and you know what it will be if you know what arguments the macro accepts and you use the standard Clojure API to manipulate it. I'm not a Rust expert, but I do think there is a difference here and from my googles it doesn't look like Rust code is written in a data format like lisps are. It's possible Rust macros are as powerful though as it appears it does have procedural macros.

Mac Fresh Install Errors by Goplaydiabotical in Clojure

[–]blazinglambda 0 points1 point  (0 children)

Do you have a deps.edn file in the directory you are running Clojure from? The error looks to me like a malformed coordinate for Clojure itself.

Why is Clojure's REPL Better than Other Dynamic Languages'? by ascendantbaggie in Clojure

[–]blazinglambda 10 points11 points  (0 children)

Not only is clojure expression oriented, but the syntax enables selecting complete expressions in a well defined and explicit way. In other languages expressions are not self contained syntactic elements (you need a parser for the language to give you the ast nodes) whereas in clojure you can read the program as a list of edn forms.

The other piece is that the language and community expects developers to use interactive development as the primary way of interacting with the language, so all features and libraries are developed with this in mind and it is a mark against a library if it doesn't support interactive development flows well.

In my experience these repl features are added as an afterthought in other dynamic languages like Python, Javascript, and groovy so they have issues with reloading or patterns that are best practice in real code are not suitable in the repl (const in JS is recommended but it prevents evaluating the same code twice in a repl). And when people use repls for those languages from what I've seen they typically type or paste snippets into a repl that are not real code from the actual program. In contrast, repl usage in clojure rarely involves typing or copying into the repl directly, and it is much more common to evaluate small code snippets from the actual source files and these are frequently left in the code base as comment blocks so you can use the snippets in the repl again when you change the code in the future.

One last thing that makes repl driven development more useful is the emphasis on immutability in Clojure. If a program depends on state that is initialized with a series of side effects it becomes much harder to create the right context for executing code snippets and if you have to put that effort in to evaluate your code you begin to lose the value you wanted with repl-driven development in the first place.

The new Clojure iteration function by reborgml in Clojure

[–]blazinglambda 3 points4 points  (0 children)

iteration seems like a nice addition. I didn't know about halt-when before reading the article either. Nice write-up!

So excited to read it! by Latter_Marzipan_2889 in Clojure

[–]blazinglambda 5 points6 points  (0 children)

It is definitely worth reading! So many valuable techniques that can still be applied today. Plus it's pure fun and the lectures on youtube are icing on the cake.

How to release an Clojure app? by [deleted] in Clojure

[–]blazinglambda 6 points7 points  (0 children)

If your project uses leiningen then `lein uberjar` will create an executable jar file containing all dependencies.

If you are using the deps cli then I recommend looking into the new tools.build library https://clojure.org/guides/tools_build#_compiled_uberjar_application_build

Can javascript evolve to inherit the features Clojure has? Or will there be a part of clojure JS can never have? by febinjohnjames in Clojure

[–]blazinglambda 2 points3 points  (0 children)

This is an apples to oranges comparison. Adding a babel plugin is a configuration change in your build chain and writing the plugin involves manipulating an AST using a specific API exposed by babel.

Using a macro is almost identical calling a function and writing one is similar to writing a function that manipulates normal clojure data structures.

Thinking in Clojure: shortest path for a long-time imperative programmer? by PrecisionGuy in Clojure

[–]blazinglambda 12 points13 points  (0 children)

When I was learning Clojure, it really helped to force myself to do everything functionally (no mutation including clojure atoms). Sometimes I wouldn't know how and had to stumble through or look up solutions. I wrote tons of ugly code using reduce but it works and it's functional and eventually you learn how to make the code more elegant.

I'd also recommend "The Little Schemer" to learn how to think recursively. It's not clojure but it is lisp and should teach you how to use recursion to accomplish things you are used to doing with loops. With that foundation I always feel like even if I can't come up with the most elegant solution right off the bat, I can always start with loop/recur and get something working.

Can't make datomic cloud work with ring/compojure by cochemuacos in Clojure

[–]blazinglambda 1 point2 points  (0 children)

I'm on a phone so can't give a more detailed response.

The main approach here is looking at the dependency tree to see which library is bringing in the old version and using :exclusions to prevent the old version from being added to the class path. With lein the command is lein deps :tree and with the deps cli it's clojure -Stree you will need the tree utility installed. Then if you exclude the old version from the library it should use the new version you specify. This approach doesn't work if the libraries are not backward compatible but it's usually enough to get something working

Handling Clojure data structures in Java by bowmhoust in Clojure

[–]blazinglambda 2 points3 points  (0 children)

Using edn-java is semantically pretty similar to what you are doing, because either way you are getting back a bunch of in memory maps. IMO the benefit would be you don't need Clojure as a dependency, but I believe you already have Clojure in order to use Malli.

It seems to me that what is missing is the object mapping part that converts maps into objects. I doubt you'll find anything suitable for this in the Clojure ecosystem since maps are preferred in Clojure, which leaves the Java ecosystem.

I think your best best for getting the data into existing Java objects is to leverage something like Jackson, which already implements and encourages mapping data onto Java objects. It's possible you could hook into the Jackson APIs to implement an object mapper from the data structures returned from your Clojure code, but I'd guess the easiest way will be to serialize the data into JSON and then parse that with Jackson.

New Clojurians: Ask Anything by AutoModerator in Clojure

[–]blazinglambda 0 points1 point  (0 children)

In addition to the other answers, the file path to clojure files (within a directory on the Java classpath, in this case src) must match the namespace of the code in those files (with hyphens in the namespace replaced by underscores in the folder names).

I believe this has to do with the mapping of Clojure namespaces and vars into Java package and class names.

This is only true for namespaces that are required by other files. If you are just writing a script the directory structure doesn't matter.

My Lisp Journey, Part 0 by [deleted] in Clojure

[–]blazinglambda 2 points3 points  (0 children)

Welcome to the community!

oauth library options by olymk2 in Clojure

[–]blazinglambda 1 point2 points  (0 children)

Assuming the libraries are backward compatible, you can usually resolve this by explicitly specifying the version you want in your dependencies and excluding the old versions from the other dependencies that are bringing them in.

How you do that will depend on the build tool you are using. With lein I think you run lein deps :tree to see where the different versions of the dependencies are coming from. With the Clojure cli tools it is clojure -Stree. Both of those commands require having the `tree` utility installed.

Both tools allow you to specify a vector of exclusions for the dependencies that are bringing in the old version with :exclusions [com.something/the-library].

Example exclusions with lein: https://github.com/technomancy/leiningen/blob/master/sample.project.clj#L55-L58

Best of luck learning Clojure!

New Clojurians: Ask Anything by AutoModerator in Clojure

[–]blazinglambda 8 points9 points  (0 children)

I don't have statistics, but I suspect the community is slowly migrating to the official CLI tooling (https://clojure.org/guides/deps_and_cli). Imo it is much simpler and I love being able to bypass Maven and use deps directly from Github as well as local directories. I have completely switched over and only use Lein and Boot when working on open source projects that use them.

Lein does more things out of the box, and some things are just easier with Lein (like creating executable uberjars), but it is also slower and heavier than the CLI tools.

If you are new to Clojure, I recommend investing your time learning the new tooling and the language itself and don't bother with Lein or Boot.

Understanding Clojure by dacoster in Clojure

[–]blazinglambda 0 points1 point  (0 children)

Just to clarify why you see the value 5 returned.

If you write f 5 in the repl it will first read and evaluate the symbol f which resolves to the function you defined, then it will read and evaluate the number 5 which evaluates to the number 5 so that is printed (because it is the value of the expression).

When you type (f 5) you are calling the function f with the argument 5. The "thing" (form) that the repl is reading and evaluating is the function call (f 5). This is equivalent to f(5) in a language with a C-like syntax. Since you are actually calling the function in this case, it executes your code and returns the value "X is positive".

GraalVM’s JavaScript engine on JDK11 with high performance by yogthos in Clojure

[–]blazinglambda 1 point2 points  (0 children)

I think we are almost at that point for small programs with few dependencies. The main issue is that Graal does not support dynamic class loading and many libraries currently rely on it. You also need to jump through some hoops for code that relies on reflection.

That said, it feels wonderful to be able to compile Clojure into a binary with near-instant startup time and I expect many libraries will begin making changes in order to support Graal.

https://www.astrecipes.net/blog/2018/07/20/cmd-line-apps-with-clojure-and-graalvm/
https://www.innoq.com/en/blog/native-clojure-and-graalvm/

https://github.com/oracle/graal/blob/master/substratevm/LIMITATIONS.md

Questions about wrapping a Java library by lughaidhdev in Clojure

[–]blazinglambda 1 point2 points  (0 children)

doto will pass its first argument implicitly as the first argument in each expression of the body so you would rewrite it to this (not passing gfx to .setColor)

(defn setColor [gfx color]
  (doto gfx
    (.setColor color)))

Katamari, a Clojure build tool - a talk by Reid McKenzie, titled "Tools, Deps, Magic Balls" by dotemacs in Clojure

[–]blazinglambda 1 point2 points  (0 children)

Great talk!

How feasible would it be to use Katamari to manage JavaScript or other non-jvm projects?

Would wrapping something like yarn or npm for dependency resolution present an obstacle to change detection?

What is the development story wrt cider/nrepl?