I can see static typing helping e.g. with compilers, low level software where every clock cycle and byte matter and some specialized stuff. I don't see much point in static typing in a language like Clojure.
I can see static typing helping e.g. with compilers, low level software where every clock cycle and byte matter and some specialized stuff. I don't see much point in static typing in a language like Clojure.
I'm not saying this as a die-hard static typing fan. I wrote Python for years, have written JS professionally most of my career, and spent 2 years writing Clojure professionally. I'm comfortable with dynamic languages. The Clojure codebase was the most stable I've been on (including typed languages) largely due to the senior team and excellent QA. The only problem I had with it was when business shifted from a subscription model to an a-la-carte model, which required manually reworking a LOT of maps to match the new requirements. This is considerably more painful in a dynamic language than it is in a static one.
There IS a downside to static typing in terms of verbosity. I've seen Java codebases where half the code was there basically because of the static typing. More advanced type systems do not suffer from that problem. I'm fine with OCaml / F# / Rust / Typescript.
Manual rework of maps seems like something that a good IDE or a small script should be able to automate, that is the point of Clojure being a data structure (EDN) after all - you can do stuff to the code as if it was data. Maybe there were specifics not allowing such sweeping changes, would love to hear more if you can share more about it.
I have a colleague which became 18 just a few days ago. He newer heard of Clojure(Script) or CSS before late last year and started actively learning it sometime after that. Jan is able to work on considerable amounts of code for our new landing page that will have some special interactive OrgPad embeds in it. He didn't have much difficulty navigating Clojure or our code base considering he did most of the work after school and on weekends basically as a learning exercise. Of course, he is getting solid guidance by the team. Our CTO Pavel has 20+ years of experience as a programmer/ software engineer and can do code reviews with Jan, so that helps. It also doesn't hurt that Jan is very smart, basically hand selected over multiple years by our CEO Vít who led a mathematics course for children/ teens and as such known Jan very well. Giving Jan a chance like this is basically a pilot combining normal education with practical, professional experience. Something like a very long internship on steroids :-)
I would still stress that static types are much less valuable than good variable/ function names and documentation/ clear code in my opinion and that maybe the complexity static types introduce isn't worth it overall. I also don't think, that static types as seen in e.g. Java or C# add any discernible degree of confidence - else somebody on the team would notice already. Jan didn't have problems navigating the code base even though most of the technologies, the language and the code base were completely new for him. Yes, anecdotal but I think it really is a valid and very important point.
My point of reference between typed and untyped code is primarily with Typescript and Javascript since that's 99% of what separates the two. For what it's worth, I do have 21 years of experience and have been lead on my projects for a number of years. As the lead, I get enough benefit from the type annotations that I've told co-workers to just any type anything they can't get working immediately and I'll fix it in code review. It's enough of an overall team velocity increase and easier code reviews that it's a win.
I'm not trying to argue with your experience. Dynamic languages work fine for building products and Clojure is my favorite dynamic language by a significant margin. I'm sure I'll write more Clojure in the future. I'm just trying to explain the benefits since I had the same mindset 15 years ago and now prefer static types. I'll mention that don't particularly like C# or Java and their type systems are not expressive enough for me to prefer over dynamic language.
> I've told co-workers to just any type anything they can't get working immediately and I'll fix it in code review
If you’re co-workers aren’t able to work with the types, how can they receive any benefits?
Not everyone is at a hyper growth place, but those that are would have zero luck with this pattern.
There's plenty of distance between unable to work with types at all and knowing the full breadth of the TS type system. I've worked with plenty of people who weren't enthusiastic about types and this is my way of handling those objections. Even someone who doesn't know/care about types gets the IDE features from the types.
I have yet to have someone persist in sending me lots of any types past two weeks because having your work constantly corrected is embarrassing. I don't expect everybody to care about the type system past generics so if someone wants to write a basic index type instead of the more correct keyof or opts for any over figuring out a conditional intersection type, I'll take care of it in code review. I'd rather have them writing code than reading TS documentation to wrangle advanced types and people do look at the commits when they go in and learn the tricks over time.
Most static typing folks have backgrounds in dynamically typed languages, and it's this experience (not dogma) that informs their preferences. The inverse (people have extensive experience with statically typed languages but developed a strong preference for dynamically typed languages) seems far rarer, at least nowadays where the canonical statically typed language is not C++.
Personally, I think one of the killer features of statically typed languages is that they make it difficult for people to write code for which the types would be very convoluted. In other words, statically typed languages provide rails which guide poor developers toward better habits (and incidentally, these same rails make it very painful to port a lot of JavaScript to TypeScript or for many JavaScript/Python developers to use statically typed languages).
From my understanding, the lisp language communities tend to select for programmers who have good habits already so they probably don't benefit from this property as much as other dynamic languages. Similarly, lisp programmers can use metaprogramming without it turning into a complete shit show, which is what almost invariably happens whenever Python programmers start dabbling in the metaprogramming facilities of their language.
That said, there are other advantages to static typing besides preventing classes of errors. For example, a lot of important documentation is generated and guaranteed by the type system. A perennial problem in dynamic languages including lisps is documenting the types. If you say something is a "file-like" object, what does that mean? Does the callee require that the object has a read() method, or does it also need seek(), close(), etc? And very often you don't even get "file-like object" but rather just a terse variable name (lisps are especially guilty of this, in my experience) or the documented type is outright incorrect (typically someone changed the code but didn't update the documentation). Additionally, you get a lot more powerful tooling, because the types can be reasoned about statically (which isn't to say Closure lacks powerful tooling, but rather that its tooling is powerful in spite of dynamic typing).
Right, this is exactly my point. People think these languages (especially as they were a decade or two ago) are representative of statically typed languages.
> Static types are at the top of the list of things that contribute almost nil and cause a huge amount of development overhead everywhere.
This is your dogma, not an established fact.
I'm glad you like Closure; no doubt it's a fun, useful language. It's probably even better than Java or C++ for many things. I'm only saying if you try different languages you might see that there are tradeoffs in features. You might still think Closure is the best, but you might at least understand why people appreciate static types.
Certainly, but there are lots of factors beyond static types vs dynamic types. For real world projects, I might similarly use Python or JavaScript over Haskell because it will be easier to find developers, libraries, and tooling. That doesn't impugn static types, however (on the other hand, depending on the project, Haskell might be the better tool).
Indeed, the type system is a relatively small, overemphasized facet of any programming language. See my comment here for elaboration: https://news.ycombinator.com/item?id=27784762
In my experience, having written Clojure professionally in three different organizations over a period of close to a decade, static typing would have helped tremendously in producing maintainable software that allowed new developers to come into a project and get up to speed quickly, and build out more features without struggling to overcome the burden of endless boilerplate and incoherent abstractions, which Clojure makes easy. Which is to say, every Clojure codebase I inherited was a mess, and the tools Clojure provides to mitigate the problems of sharing a big codebase among a team of programmers are insufficient and place a tremendous burden of manual labor on industrial programmers--especially spec, which ironically produces exactly the kind of boilerplate pain and maintenance labor that Clojure apologists blanket-accuse statically-typed languages of producing. This directly and meaningfully impacts productivity and efficiency, and of course helps amplify bug creation as well.
> If I pass garbage to a clearly named function, I get garbage out as with the static type system.
Yep, and for the most part the only way to figure out what functions receive or return in Clojure is to either read the code, or actually call them. Relying on other programmers to be consistent and coherent in their naming is not really a great long-term approach...go figure. And, note that a side effect of this is that you often don't find out until some code is in production that you are passing garbage data around. AND that's leaving aside how badly most libraries are documented--even core libraries, how type information is rarely if ever shared in library documentation, and how Clojure doesn't enforce any kind of purity on functions so that basically, any function called anywhere can be doing basically anything.
Really it's not even about not having static typing--which is not a silver bullet, and absolutely has tradeoffs--but the fact that the community seems fundamentally immature and unable to discuss software engineering topics (static typing especially) without turning it into an emotionally laden battle between tribes, vs. a comparison between different tradeoffs. Simple vs. easy indeed.
I really did enjoy the language at one point, and credit it and RH for introducing me to the world of functional programming, but at this point I doubt I'll ever write Clojure professionally again. However if I do, it will be with a team that understands its strengths and weaknesses without attachment to emotionally-driven arguments about static typing, or whatever.
With great power comes great responsibility. The things you describe all seem like a problem of inheriting a code base written by people without much organizational discipline ("was a mess") or working with people ("Relying on other programmers to be consistent and coherent in their naming"), that don't have a coding discipline. Such things should be caught in a code review or perhaps cleared at the design stage. How do you want to work with people, that are not able to communicate their intents clearly to a human? How do these people "talk" to the computer through their code, are they partially guessing the "correct" code? Are these programmers perhaps producing a mess independent of the codebase or language?
I am really sorry for you, it must have been hell. I know Clojure(Script) isn't the silver bullet many people make it out to be. The programmer still has to be quite competent and quite disciplined at times but Clojure can and will support you - that is my experience. I have seen my fair share of stuff, professional programmers with two decades of experience that couldn't use a profiler and would dismiss clearly debugged issues, where they initially pointed fingers at the database and the network. It is quite easy to end up in such a state of incompetence - you don't do very demanding stuff or do the same stuff all over again for 20 years and then comes the advanced problem, you are under pressure, because the deadline has slipped again for a year and you are totally lost in the reflection hell you have created. I have other similar experiences with "professional" programming. Our CTO Pavel has a number of good war stories about static types, avoiding static types and problems created by OOP (classes are types too in my book) too.
Well, some libraries could use better documentation. So could Java, Python ... libraries. The point in Clojure is, you often can just read the whole function because it is like 10 lines of code or the whole library that often isn't that long either (200 lines of code isn't that uncommon). There are core developer on Slack basically every day, so you should be able to catch them and answer questions more or less quickly. And yes, I have had my fair share of problems with libraries too, you can check on Slack if you want - so I sat myself down and have rewritten the part that I needed more in line of our code. It is more robust as it has checks specific to our use case and we have one less dependency we would only use for 10% of its code.
In general, I think Clojure is a great language. It is very powerful and can be a chalange to wield without any kind of insightful guidance. Once you have crossed the initial steps though, you will probably surpass the productivity of most other languages even in a largish team.
They come in different forms. Some can rely heavily on inference (Typescript) and some need to be very verbose (Java)
There are structural typesystems (Typescript) and nominal typesystems (Java)
In a structural typesystem the contents of a type can be compared to another type and match, whereas in a nominal typesystem it has to be the same type id to match. The former is more dynamic than the latter.
I don't like Javas typesystem but I like Typescripts typesystem because it's more flexible and doesn't force you to be verbose (you can for example only type function arguments and let it infer the rest)
One benefit I haven't seen mentioned here is the ability to refactor code with confidence and less need for unit tests.
For example comparing Javascript to Typescript; If I change a functions argument that is used in many places, the typesystem will give me a list of other code where the function is used I need to change in order for everything to work again.