HN user

huahaiy

342 karma

I am a technologist and an entrepreneur, with an academic background in psychology and computer science.

Posts11
Comments185
View on HN

I am glad you enjoyed it. I am happy to report that the next release will have many new features: Raft consensus based high availability (comes with an extensive Jepsen test suite); built-in MCP server; built-in llama.cpp for in DB embedding; JSON API; language bindings for Java, Python and Javascript.

I have been using AI to write Clojure code this past half year. The frontline LLM has no problem with writing idiomatic Clojure code. Both Codex and Claude Code fix their missing closing parentheses quickly. So I won't say "Writing Lisp is AI resistant". In fact, Clojure is a great fit with AI coding agent: it is token efficient, and the existing Clojure code used for training are mostly high quality code, as Clojure tends to attract experienced coders.

Why Clojure? 1 year ago

I wrote my library this way for both performance AND type checking reasons. You cannot tell me the reasons why I wrote my code my way. That is just absurd.

In any case, it is possible to write Clojure in a way that is type checked at compile time. And it is an acceptable way to write Clojure. I just want to clear the air.

Why Clojure? 1 year ago

But if you add type hint in the signature, it does check the type. Basically, if you specify the type, it will check type. Just like any language that is not automatically inferring types, e.g. Java. So it is the same as Java.

You guys make it out like Clojure is doing something extra to hide Java types, but it doesn’t. What Clojure does is really minimal on top of Java. It barely hides anything.

If you give it type, it will check type. If you don’t give a type, it falls back to a default type, Object, which IS a TYPE. The fact that Clojure compiler cannot deal with GraalVM SVM Pointer type tells you that it’s checking type, because Pointer is not an Object! I found this out the hard way: https://yyhh.org/blog/2021/02/writing-c-code-in-javaclojure-...

“One limitation that one needs to be aware of when writing native image related Clojure code, is that most things in the GraalVM SDK inherit from org.graalvm.word.WordBase, not from java.lang.Object, which breaks the hidden assumption of a lot of Clojure constructs.”

Why Clojure? 1 year ago

That has nothing to do with types. You are now talking about when a compiler is run.

Clojure gives you the option to run the compiler at runtime, so that's what people normally do. However, you can also run the compiler at compile time. Right?

For people who want type checking, they can opt to write Clojure in this defprotocol everywhere style, and turn on AOT. Then they basically get the same thing as what Java gives them.

As to example of libraries that are defprotocols everywhere, you should look at any of the low level performance minded libraries in the Clojure ecosystem, they are either written in this defprotocols everywhere style, such as nippy, neanderthal, dtype-next, and so on, or mostly in Java, such as http-kit, fast-edn, etc. I noticed this phenomenon, because my own libraries, editscript and datalevin, are written in this way. I take comfort that my fellow performance minded library authors are doing the same. Finally, isn't Clojurescript entirely driven by protocols?

So really, there are two kind of Clojure programmers. One type writes application code or high level libraries, and they write normal Clojure code all the time. However, there are also those who write primarily low level library code, which are used by the first camp, and their code is full of defprotocol and deftypes. So defprotocol everywhere is not anti-pattern. It's anti-pattern only in the mind of the first camp of programmers, and that's a narrow minded way of looking at things. Even the first version of Clojure Programming book by the core team members, are written in a way that's full of defrecord. Remember?

This "everything is a map" orthodoxy is turning people away from Clojure. Just let people write the code that suits their own needs. We can use more people who are pragmatic instead of dogmatic in the Clojure world. If you trust Rich Hickey's judgment, then you should trust him put in the features Clojure has for good reasons. Macros and protocols are part of the Clojure language, and you should be using them when the use case calls for them. Stop the "anti-pattern" nonsense.

Why Clojure? 1 year ago

deftype IS a Java class, it's not compiled into something else. What is a Clojure function? A Clojure function is a Java class. Clojure is a compiled language, so it does check types, just like Java check types.

So if you use defprotocol and deftype for every domain objects in your code, your code won't compile if there's a type error. Try it.

BTW, that's the way many Clojure libraries are implemented. These libraries rely on dispatch on type to work, so they are taking advantage of the type checking.

Of course, you will say, "oh, clojure is not normally AOT, so it's not dong the checks.", but that's another issue. The issue at hand is this: can you write Clojure such that types are checked at compile time. The answer is YES.

The compiler may run only when you run the program, that's a different issue. You are confusing these two issues.

If you want a separate compile stage, then basically you are already excluding runtime compilation, i.e. you are arguing against runtime compilation. So it's not really about typing, but about how you want to run the program. Isn't it? You want AOT for everything, you don't want runtime compilation. That's it. It has nothing to do with types.

Why Clojure? 1 year ago

Then you are not writing in defprotocol everywhere style. The keyword is everywhere. All the domain objects are deftype or defrecord. Try that. It is the same as Java, basically.

It is not an anti pattern, it is the way most low level libraries and clojure itself are written.

Clojure is a tool, not a cult. This core team worship is turning people away. The core team made plenty of mistakes, and got called out, rightfully.

Why Clojure? 1 year ago

No, defprotocol and deftype have the same properties as Java interface and Java classes, and the types are checked at compile time. This is static typing. Period. Clojure is a compiled language, it does check typing during compilation.

Why Clojure? 1 year ago

Nobody would argue that Java is not statically typed. That's my point. Clojure offers the same as what Java offers.

If you write code in a defprotocol everywhere style, as many Clojure libraries do, your code won't compile if you got the types wrong. The same as Java. How's that not static typing? Which part of that is weaker?

So what exactly this "Clojure dynamic typing" nonsense is about, I fail to see.

Automatically inferred type system is not the same thing as static typing. Typed Clojure is the former. Typed Clojure did not catch on, but static typing style of Clojure did, as many Clojure libraries do exactly that: internally, defprotocal everywhere, externally, some Clojure functions to give the illusion of normal Clojure code. BTW, that's the style how Clojure itself is written in as well.

Why Clojure? 1 year ago

I thought distributed system is exactly one of the things that got Clojure going to begin with. Remember Storm?

As to databases, there are a fountain of databases emerging from the Clojure ecosystem: Datomic, Datascript, Datalevin, XTDB, and friends aside, there is Rama, which is a distributed database (or to replace database?)

Why Clojure? 1 year ago

You can still do lein new and away you go, today. That still works.

Many people still use lein for new projects, especially for larger ones. For small on-off thing, clojure command line is more convenient. So it is a good thing to have more choices.

Why Clojure? 1 year ago

REPL code is copy/pasted straight into tests. So really, REPL is for helping write tests.

BTW, when Clojurians talk about REPL, it's not about that separate window where you type and run the code as in other language such as python. They are talking about an invisible REPL running behind the scene, to which they send code within their editors, and the results show up in the editors too.

There's no need to "miss static typing" in Clojure. If I need static typing, I just write deprotocol and deftype in Clojure, as many Clojure libraries do.

Why Clojure? 1 year ago

Scales very well.

Immutable data pairs great with REPL.

Because data are immutable, you don't need to care about where that data come from and where it will go. Just focus on what you need to do with that data at the point you work.

Everything is localized due to immutability.

Why Clojure? 1 year ago

Not really. Database is where immutability may not be a good fit, at least not all the time.

In many use cases, database is where application state resides, hence a mutable database is a better fit.

There are Datomic flavored database in the Clojure ecosystem that is specifically design to address this point, e.g. Datalevin.

Why Clojure? 1 year ago

For someone who needs static typing, Clojure give them defprotocol, deftype and defrecord.

Interestingly, most of the low level libraries in the Clojure ecosystem are programmed that way. If you open up the code of many popular Clojure libraries, you see defprotocol everywhere.

So really, Clojure does both: dynamic typing for application programming, mostly static typing for infrastructure library code. As they should be.

This tired argument of "Clojure bad for dynamic typing" just doesn't hold water. Keep repeating it is a sign of lack of critical thinking.

No. I have looked at your code. You did not use the mentioned https://github.com/clj-easy/graal-build-time

If you don't do what everybody is doing to solve a problem, then of course it is not a "solved problem" for you.

No, you don't need to code specifically for native-image. What are the landmines that you need to code around? Since you have not successfully compiled native-image by following common practices, you obviously don't know.

Graalvm native image for Clojure is a solved problem. Just add this library to the project and add a flag to native image command line.

https://github.com/clj-easy/graal-build-time

This initializes Clojure classes at build time and it mostly works for pure Clojure code.

Doing complicated things (e.g. depending on native library, etc.) requires some tweaking. For example, a few packages may need to be declared as initialized at build time or run time, depending what they are doing. And any unresolved classes need to be added to reflection-config.json.

All these are easily discoverable if one talks to people in the Clojurian slack channels. Clojure is a small community, so it helps to be part of it, because there are not a lot of open materials on the Web.

Correct. I don't know about DataScript's intention, but it is intentional for Datalevin, as we have tests for sequential intra-transaction semantics.

Not really.

Our experience is that Clojure is a very good language for newcomers to programming as a profession.

In fact, we almost exclusively hire new computer science graduates. None of them had heard of Clojure before joining, and the vast majority of them became useful in 2 weeks, and become productive in a few months. What you described as barriers are the things that got sorted out in the first day when they join.

We do not hire veterans unless they already know Clojure. These people need to unlearn stuff, some of them are resistant to changes, so we don't bother with them.

Rust and Clojure address completely different domains of problems.

Rust is competing with C++ for writing low level systems, whereas Clojure is designed for writing business applications.

Low level systems tend to have stable and well understood requirements, whereas the same cannot be said for business applications. Efficiency is not a primary concern of business applications, but flexibility is. The efficiency of Clojure is often "good enough" for business application, and there is no peer in flexibility. This explains the many fintech companies using Clojure, as well as many other B2B companies.

Most of these things do have Clojure libraries. For example:

- unit tests with ephemeral dbs: we use com.opentable.components/otj-pg-embedded for postgres.

- db migrations: there are Migratus and many others, we also just use flyway in app startup shell script

Some are not relevant if you write proper Clojure.

- ORM library: I am not sure you need these in Clojure. Clojure is data-oriented, not object-oriented, so you don't need to map data to objects. Clojure is good at transforming data. Just keep data as data.

I don't recall us spent too much effort in building Clojure libraries of our own for something missing.

For my over a decade of Clojure career, I only built two Clojure libraries of my own. Both were not missing, just that I didn't like the existing ones. One is a Clojure data diff library (Editscript), and another is a database (Datalevin).

In general, if one cannot find a Clojure library (very rare), we tend to use Java interop. As mentioned repeatedly, Clojure is a hosted language and we embrace the platform.