I'm not the biggest Java fan, I can tolerate it. Recently working on a Python project I find I'm massively unproductive compared to something like Java or more specifically Scala as using Spark.
Not having strong types and limited type hinting. I have no idea what things are, is it a int, string, object etc, clicking through in an IDE to see code/docs isn't as great as it's hard for an IDE to inspect the code compared to strongly typed language.
Not having a compiler means I have to have unit tests doing what a compiler would do or finding out I have syntax errors or other errors at runtime. This massively throws of my development flow as can't lean on the compiler to find trivial errors. Rather than doing compile I have to hope I have a test case with high coverage to instrument the code to get a syntax error or I have to start / deploy it to get the error.
There's a great deal of issues with dependency management. Venv, Docker etc help with this but it's a bunch of extra stuff on top I now need to worry about and sometimes still hit errors.
The only time I find I'm relatively productive in Python is writing a short 100-200 line script to do one off tasks / sysadmin / devops type scripts.
The fact if I change an interface, rename a method I need to recompile I find a huge advantage as everything breaks and I worth through the errors one by one and once complete and no more errors I have confidence the refactor is complete and the compiler has verified it.
Types almost never save the day and they don't add much to the understanding. The code should be structured and documented in such a way that you are able to understand it, reason about it and swap implementations of stuff in the places where such flexibility is handy. E.g. in Clojure, I can trivially test my functions in the context of the application using the REPL. I don't have to reload anything, I do it right in the namespace I am modifying. I can also just look at the data there.
Also Clojure is way more consistent and you can probably learn all the functions in clojure.core and some of the typical libraries by heart. These functions then work basically on anything you will do and are almost 100% transferable to ClojureScript.
For sysadmin-like tasks, for most stuff you can use Babashka, which is a limited Clojure + some frequently used libraries implementation with very fast startup thanks to GraalVM. For the rest, Python/ Perl etc. will probably still have a bit better standing because of all the libraries e.g. working with SNMP.
> Types almost never save the day and they don't add much to the understanding.
There’s a saying in strongly typed languages “make illegal states unrepresentable”.
Java doesn’t let you get all the way there but it’s better than nothing.
When you look at strongly typed languages with a good type system, often when you compile it just works.
Besides hello world, I’ve never written program beyond a few lines that has just worked in a dynamic language.
...I do like clojure and the repl workflow and how everything is data. Haven’t done much besides play with it but have a friend who writes clojure professionally on a large codebase and their feedback is most of the errors they now see wouldn’t exist with a type system.
Can you be more specific about your friends codebase? How large, what industry/ problem space?
Of course, you can write bad/ unmaintainable code in any language. I have seen such code in Clojure(Script) as well. But some languages really encourage bad code where other languages already feel like you are doing something wrong when you write bad code. (e.g. Using many atoms in Clojure, doing boolean transformations etc. just from the top of my head.)
Strongly typed languages in my experience don't help anything, make programming harder for everybody and the benefit is (for most software) questionable. Compilers should handle most types for us and only give us a hint something could be better, if we were more specific e.g. with a type hint. Humans should think about the problems not about deep implementation details (like integer vs float vs double), when not strictly needed.
Schema systems (like spec and malli in the Clojure world) work well for checking states in data and are more powerful and ergonomic for that than type systems in mainstream languages.
That's strongly dependent on how big the code base is.
I'm a huge python fan, and I don't like/use IDEs on my personal projects. But eventually I still gravitated towards typescript because it had typing and the compiler + unit tests giving me a huge boost in ability to extend my project beyond a certain size.
I think the threshold is around 50-100 files, but smaller code bases also benefits from the typing structure.
At OrgPad, we are well over 100, maybe even 200 namespaces in Clojure and ClojureScript and I don't think we have any trouble keeping up. Most of the problems we have are frankly not in any capacity connected to Clojure or ClojureScript but rather half-baked, inconsistent technologies and their implementations like CSS, browser interoperability/ APIs mostly. The server-side (Clojure) is rather boring currently, so can't say much about that - most of the stuff seems to be just ok. There seems to be a lack of libraries for transforming image/ video formats in Java for the newer formats like WebP, AV1. Probably most people drop to imagemagick/ ffmpeg? That is kind of a problem, but not really pressing. We have lost much more time to CSS and bad browser APIs.
> I have no idea what things are, is it a int, string, object etc,
What if it's some kind of a String? Only a String starting with "_". You may define UnderscoreString. But now it's not obvious what this is. You have to go look it up either way. A compiler may stop you from passing a regular String. If you're very lucky it may even stop you from casting an obviously wrong literal. But beyond that you're probably out of luck unless it's a crazy language. Once everything is a custom type how is remembering all the types different from remembering what each function does?
I'm not sure how unit tests help here much either. Why would you come up with an example that breaks your code in a unit test but couldn't think of it beforehand? Unit tests are used just as much in environments with a helpful compiler. They mostly help stop new breakage affecting stuff that used to work.
I wouldn't write off checking at runtime. You can actually define exactly what it is and it's easier. You can do as little or as much of it as you want. It's not compile time but depending on your software you may be able to get fast feedback. This level of checking would not be compile time either way. If you really want to be sure you'll be doing this in your typed language too. You're only worse off if you'd really benefit from the simple stuff.
If wrong data hitting a function can cause multi-million dollar loses would a single person think the compiler is good enough? What if it can cause major data loss? Big embarrassment? Pretty clear to me one will only trust the compiler with bugs that don't matter in the first place.
> Not having a compiler means I have to have unit tests doing what a compiler would do or finding out I have syntax errors or other errors at runtime.
Nope, it's not the only way. In practice you'd have type hints all over your code and a linter to perform static analysis. Of course this can be integrated in you IDE, so it's easy to do.
> Not having strong types and limited type hinting. I have no idea what things are, is it a int, string, object etc, clicking through in an IDE to see code/docs isn't as great as it's hard for an IDE to inspect the code compared to strongly typed language.
Again, with type hints you'll find your IDE does a pretty good job. Heck, Pycharm even gets it right without type hints sometimes.
Not that I don't see the value in compilers, but the case is not as clear cut as you expose, far from it.
> There's a great deal of issues with dependency management. Venv, Docker etc help with this but it's a bunch of extra stuff on top I now need to worry about and sometimes still hit errors.
Not sure what Docker has to do with Python here... I don't use it and we're doing fine. Venv is something you have to learn, sure, but that's what it takes to become productive on any platform, be it Python, Java, Go, Whatever: learn the good practices associated with it.
And while the dependency management is not stellar with Python, that's really not something to love from Java either.
Type hints are very different to a strongly typed language with a good type system. Haskel as an extreme feels like algebra, you have a type a, you need a type c, you have a function a to b, and a function b to c, you just compose them together and from your a you have a b. Tools like hoogle can tell you the function to use for the given args to get to the final type you want.
Ignoring Haskel even Typescript can let you express things hard to express in Python with ADT’s etc.
Docker has nothing to do with Python but if you want to produce an artifact you can just run anywhere it solves the dependency issue in that the decencies are in the image and you don’t need to run pip install on a deployment server.
I'm not the biggest Java fan, I can tolerate it. Recently working on a Python project I find I'm massively unproductive compared to something like Java or more specifically Scala as using Spark.
Not having strong types and limited type hinting. I have no idea what things are, is it a int, string, object etc, clicking through in an IDE to see code/docs isn't as great as it's hard for an IDE to inspect the code compared to strongly typed language.
Not having a compiler means I have to have unit tests doing what a compiler would do or finding out I have syntax errors or other errors at runtime. This massively throws of my development flow as can't lean on the compiler to find trivial errors. Rather than doing compile I have to hope I have a test case with high coverage to instrument the code to get a syntax error or I have to start / deploy it to get the error.
There's a great deal of issues with dependency management. Venv, Docker etc help with this but it's a bunch of extra stuff on top I now need to worry about and sometimes still hit errors.
The only time I find I'm relatively productive in Python is writing a short 100-200 line script to do one off tasks / sysadmin / devops type scripts.
The fact if I change an interface, rename a method I need to recompile I find a huge advantage as everything breaks and I worth through the errors one by one and once complete and no more errors I have confidence the refactor is complete and the compiler has verified it.