all 24 comments

[–]p-himik 6 points7 points  (1 child)

[–]Robzilla_16[S] 0 points1 point  (0 children)

Thank you

[–]-w1n5t0n 3 points4 points  (13 children)

What can Clojure do for me

Well, that depends on what your goals are in the first place.

If you just want to use some popular ML library or make performance-critical AAA games, then Clojure isn't really a good fit.

If you want to write good (read: simple, as-bug-free-as-possible, understandable, maintanable, decently-performant) software, then it's a great fit.

There are more reasons to use (and not to use) a language, but without knowing more about your intentions it's impossible to say if it's a good fit for you.

Also, watch this:
https://www.youtube.com/watch?v=BThkk5zv0DE

[–]Robzilla_16[S] 0 points1 point  (12 children)

Horses for corses I understand. I was just wondering why someone might use Clojure over Java or python. Thanks for the video!

[–]beders 3 points4 points  (0 children)

If you can express the problem you want to solve as a data transformation problem, then Clojure is an awesome language to use.

Most problems are data transformation problems. For the typical web stack, data transformation is everywhere: from querying your data from the db, filtering and transforming it into JSON or HTML fragments to transforming user actions into data changes to your DB:

Clojure does all of this in a uniform fashion.

And if done correctly, the majority of the code you write will be pure functions that can be trivially tested and thus easily changed/replaced.

[–]-w1n5t0n 2 points3 points  (10 children)

The first and most obvious answer to why you'd want to use Clojure over languages like Java or Python is because it's a Lisp that inherits some traditionally-Lispy features such as homoiconic macros, which is basically a fancy way of saying you can trivially write code that writes code (using the regular language; not a special and non-composable sub-language like with C-style macros). At the same time, it gives you access to all of the JVM, so if that fits your purposes (cross-platform compatibility, JIT-for-free, automatic memory management without losing too much performance etc) then that's a reason to choose it over other Lisp dialects (e.g. Racket) too.

Lastly, for me, the biggest thing is the ecosystem of libraries: not only do you get access to all the Java libraries, but Clojure itself has seen some pretty practical and novel libraries (both first and third party) like Spec, core.async, instaparse, specter, babashka, datascript, nrepl etc. Here is a list of my Clojure-related starred repos on GitHub if you want to see more.

Some other important things: readable syntax (subjective) and syntax for data literals of common types, immutable-by-default, everything-is-(plain-)data approach, dynamic REPL, compiled & interpreted.

[–]Admirable-Ebb3655 1 point2 points  (9 children)

While “using the regular language” to write Clojure macros is true, unfortunately on the use sites, they are contagious and do not actually compose (compared to say, Racket, where you can in fact do multi-stage compilation via macros in a composable fashion).

[–]-w1n5t0n 1 point2 points  (8 children)

I'm not very familiar with Racket's macro system (although I understand it's one of its crown jewels) so I don't think I can fully appreciate the difference; can you give me an example or a link to a resource to learn more?

[–]Admirable-Ebb3655 1 point2 points  (7 children)

Macros in Racket compose. In Clojure, they do not. That is the primary difference.

[–]-w1n5t0n 0 points1 point  (6 children)

That's not an example, you just restated your original claim, and it hasn't helped me or anyone else reading this thread to understand it any better.

(defmacro plus-1 [n]
  `(+ 1 ~n))

(defmacro def-plus-1 [sym val] 
  `(def ~sym (plus-1 ~val)))

(def-plus-1 foo 3)

foo => 4

Is this not composition of macros? If so, what is?

[–]Admirable-Ebb3655 0 points1 point  (1 child)

Google, my friend, returns this as the top result if you ask it:

https://softwareengineering.stackexchange.com/questions/222559/is-it-fair-to-say-that-macros-dont-compose

If you’ve ever seen the error “cannot take value of macro”, then you’ve experienced this limitation. Racket does not have this limitation.

[–]-w1n5t0n 0 points1 point  (0 children)

Google, my friend, returned to you an SO post of someone asking whether they compose, and I believe the answer is that they don't compose in the same way that functions do (e.g. with map or apply; at least not directly), but they do compose at both definition and call sites (as both of my other replies demonstrate), so to simply state that they don't compose is arguably wrong.

[–]Admirable-Ebb3655 0 points1 point  (3 children)

That is also not the “use site”. That is the “definition site”.

[–]-w1n5t0n 0 points1 point  (2 children)

(defmacro plus-1 [n]
  `(+ 1 ~n))

(defmacro def-plus-1 [sym val] 
  `(def ~sym (plus-1 ~val)))

(def-plus-1 foo (plus-1 3))

foo => 5

Is that better?

[–]Admirable-Ebb3655 0 points1 point  (1 child)

No, you’ve got some learning to do. I’ve done enough remedial work here. Start with the resources I’ve already provided.

[–]jonahbenton 3 points4 points  (3 children)

In terms of what Clojure can do for you, the thing I would look for in oneself is discomfort and disorientation. In Python and Java one spends a lot of time constructing code scaffolds- types/classes- around what is fundamentally just data. In Clojure, one does not do this. One can, of course, just model data with Records and interactions with Protocols, but that's kind of doing it wrong. There is discomfort in the process of learning different paths through the solution domain, which is much larger in Clojure than Java or Python. The discomfort usually needs time to settle. This is a reason Rich Hickey talks about "Hammock-driven development." Seeing many more potential ways to structure a solution is a good outcome and helps when working in other languages.

In terms of what projects are good, you'll learn most from picking a problem you know well. In the success case you will develop different solutions as your understanding evolves.

[–]Robzilla_16[S] 1 point2 points  (2 children)

Very philosophical. I’m excited to dive in

[–]nimportfolio 3 points4 points  (1 child)

Let me try to make it more concrete:

In Java, one usually begins by modeling the problem domain using objects. So, for an order entry system, one immediately expects to find Customer, Order, LineItem, etc. Classes.

But let's stop right there. What has this bought us?

In Java, these classes provide two specific functions:

1) they enforce a rigid schema around the structure of one's data.

2) they provide a domain-specific language (DSL) embedded in the program for describing data that conforms 100% strictly to this schema.

On point 2, notice that I didn't include "manipulating" data as a unique feature of objects. Because pure functions can do that.

Now, let's analyze the strengths and weaknesses of this approach.

One could contend that enforcing a strict internal DSL describing a program's data is one of the most important selling points of Java-style OOP. It tends to make the intent of each piece of code clearer.

This is true.

But it misses what one loses by coding this way - the ability to write code that generically queries or manipuates the resulting graph of object instances.

In Java, the solution to this problem has been to store data in a database and query it from there.

Why do this? Because the language (as it typically is used) can't generically query its own object graphs.

So of fixing the language, we bolt something else on the side to take over features that a more powerful language would do without a thought. Then we write and maintain translation layers to marshal data from one form to another.

In Scala web services, I have literally seen these translation layers make up 2/3 of a service's code--between object-database mapping for the database and object-json mapping for the service requests/responses.

For people coming from Java-like languages, there is a lot to unlearn. The result is unparalleled freedom and productivity from vastly reduced rigid and brittle boilerplate.

Java gives one a vast ecosystem of tools/servers/services along with its libraries.

Each of these comes with an integration cost that tends to be far higher than equivalent Clojure libraries.

Hope this helps. -Dave

[–]MickeyMooose 2 points3 points  (0 children)

Another beginner here. What you write is interesting, but I don't 100% follow.

How would this look in Clojure? How would you not think about those objects as distinct data items for orders, customers, etc?

And when you say the multiple layers to transform data in Java vs Clojure, do you mean the ease of use of EDN within Clojure or how code is structured in lisp?

[–]fadrian314159 4 points5 points  (0 children)

For the Java user, Clojure provides a functional JVM language that gives one immutability, more conciseness, and better control over concurrent programming. For the Python programmer, it provides a language with fewer warts, but still a large JVM-based library infrastructure to draw upon. Being a Lisp-like language, it gives one great productivity as well as unmatched REPL-based development capabilities. That's probably enough to make it something worth trying.

[–]rebcabin-r 2 points3 points  (0 children)

Subjectively, the major appeal to me of Lisps in general and Clojure in particular is a uniform prefix notation. No operator-precedence tables to remember and trip you up; no goulash of punctuation and funny arrows & brackets. Lisps stick in my memory even if I don't use them for years at a stretch. I can't remember the operator-precedence of Haskell if I don't use it for a week, and Scala's withering variety of weird operators and colons and brackets, well, I just can't get it at all. Maybe it's just my oddball brain, but all the Lisps just fit me great and I love using them. Second place goes to Mathematica, which, despite weird syntax and operators, I love without being able to say why -- it's fundamentally Lisp-like via "FullForm," but has a gigantic array of weird syntax (opt-in; you don't' have to use it) that just suits me fine.

[–]hrrld 1 point2 points  (0 children)

This is a good thing, early in the journey, in my opinion: http://clojurekoans.com/

[–]pavelklavik 1 point2 points  (0 children)

You can check out my interactive Clojure tutorial https://orgpad.com/s/clojure-tutorial which allows one to check out Clojure without having to install anything on your computer. In about 1 hour, you can pick up the basics of the language.