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.
> 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.
No-one has the time to learn all these languages, so for those of us not in the know, the most generous we can be is to take these praise articles at face value. And these articles are typically 'dynamic good', 'static bad'.
From the article:
they insist on a statically typed worldview leading to parochial, snowflake APIs that defy abstraction and higher level manipulation, or both.
So I guess the user base for this language is those developers who think dynamic is better than static, and go for (as-static-as-Java) Closure.
That's not quite true though. Java tracks types in the signatures of the functions, defprotocol does not. If I make a protocol and then pass a wrong type as a parameter to it then I'll get a runtime error. It's not going to tell me that I passed in a wrong type at compile time.
I find using defprotocol in Clojure tends to be an antipattern because it just makes code harder to read by introducing indirection. The libraries using defprotocol use it to solve a specific problem of creating a contract for how the API looks.
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.
Even if you did, that wouldn't solve the problem because many checks are still done at runtime. Also, if you started doing that then you might as well just write Java at that point. I've worked on code bases structured in this way and it's absolutely terrible to work with. For one, protocols completely break any sort of REPL driven development.
Most libraries are absolutely not written in this way either. Please point me to a single library that's actually written in the style you describe. The use of protocols in actual popular libraries like Ring tends to be minimal.
The reality is that dynamic typing has never been a real problem in Clojure. I've worked with the language almost exclusively for over a decade now, and I maintain a number of popular libraries, like Selmer, with millions of users.
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.
The whole premise of static typing is that type errors can be caught at compile time. Java type system allows for kinds of errors to be caught at compile time that will not be caught by using protocols in Clojure. Yes, you can run the compiler at run time, but that's entirely besides the point here.
These libraries aren't using protocols for type safety though, they're using them as a performance optimization. That's certainly a perfectly fine reason to use protocols, and I agree that it's a completely legitimate use case. It's the whole right tool for the job thing. If you're writing something where performance is the top concern, then that's what protocols are for.
I very much agree with you that that there are people who focus on low level code, and those who focus on application level code. The style of coding will be different depending on the type of problem you're solving. You're right that I failed to qualify my original statement regarding protocols being an anti-pattern.
You're absolutely correct that we should take a pragmatic approach towards using language features. Hence why the context of whether protocols are the right tool to each for lies in the type of code you're writing. And of course, some people find it easier to have more structure to help with their reasoning. Although, I'd argue tools like Malli work better there.
To sum up, I'm not arguing against protocols being useful or that there's no place for them. We started this discussion talking about whether protocols provide equivalent guarantees to Java's type system. I disagree regarding that. However, I also don't think that this is a real problem. Otherwise, use of something like core.typed would've become prevalent by now.
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.
I'm not telling you the reasons why you write code the way you do. I'm just telling you about my experience of using the language, which clearly differs from yours. I never found protocols to be useful as a type checking tool, and I find they're not necessary for optimizations most of the time. I spent a lot of time profiling and optimizing Selmer, this is the only protocol that turned out to be necessary for performance https://github.com/yogthos/Selmer/blob/master/src/selmer/nod...
If you find protocols are a helpful tool to structure code that's great, keep using them. Nobody is telling you not to. However, try to accept that different people use the language in different ways. Try to follow your own advice and not to be dogmatic about it.
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.