More on the topic I can say that I don’t really see anything compelling that you can’t easily do in F# with a lot more safety in this article...
More on the topic I can say that I don’t really see anything compelling that you can’t easily do in F# with a lot more safety in this article...
Any project of this size is too big and is better off being broken up.
You need tests no matter what kind of typing you are using.
The best thing about Clojure is that it is immutable by default. Start writing pure functions that transform you data step by step until you send it on its way with a side effect. I don’t miss types much at all - immutable maps lists, sets and pure functions get the job done and it is fun to program.
Static typing fans hardly ever acknowledge how difficult the types make it to read the code. Too much focus on that one time when someone made a typo and there was no unit test.
This has been argued infintum, nobody is changing their mind. WTF am I doing here?
let add n1 n2 = n1 + n2
Where do you spend time reading the types? Though it’s fully typed and if you pass an int and a string or some type for which the + operator is not defined you’ll get a compile time error.I think types make code much more understandable. I like them to be explicit so there's no mental overhead of thinking "what type might this be" - and the larger the codebase, the more this pays off.
There were really 2 main points to java (as I understand it). The first was to have a statically typed language that had a relatively fast runtime that could easily be ported to many platforms. Indeed, the killer application for Java, IMHO, was the idea of implementing that run time in hardware (and it's a shame that Sun never really pushed that angle very hard).
The other main point of Java was to codify a set of "best practices" as a kind of "defence against the bad programmers". The intent was to reduce the difficulties of hiring programmers by making the programming language less expressive (compared to C++, for instance). IMHO, this largely failed as programmers are creative and will always find ways to be expressive. So on one had we got C++ template crack and on the other hand we had abstract factory factories.
Basically, though, I don't think there was ever a real intent to allow Java (the language) to make it easy to scale development to larger teams, or larger code bases. The success that Java had in getting into large enterprises just meant that the normal Java user was interested in that, because it's the environment that they were working in.
The idea is attractive to enterprise people because they already have a lot of people and they want to get a lot done in a short period of time. Most managers (and many influential programmers) do not understand the loss of productivity that accompanies communication overhead from having larger teams. The large code bases are the necessary result of programmers trying to isolate themselves from the hundreds or even thousands of other programmers that they are working with. By building complex boundaries, they can reduce the communication complexity at the cost of increasing the program complexity. This is usually a good trade off if you are stuck having to work with an insane number of people.
BTW, in case you think that it is only non-technical people who fall into this trap, I will provocatively mention 2 words: micro services. The way micro services are usually designed and implemented, they are usually the very definition of premature subsystem decomposition. This premature decomposition is chosen so as to allow a few people to "lock in" design decisions and reduce the need to communicate with a whole bunch of people who might ruin their architectural vision. Basically, same problem, same result. Technical people (who didn't live through the CORBA years) drink it up like the yummy, yummy cool aide it is, though ;-)
Technical people understand that a technology that can drastically reduce LOC is a game-changer: you can hire far fewer programmers - these need to be better than your bucket-brigade Java folks of course, but that's OK because you're saving money on the quantity of them; you need fewer managers to coordinate everyone; the product itself will be better because it's had fewer fingers in the pie, and internal comms during development will have been better because the team was so much smaller. No, the big problem with this is that management in big corps simply do not believe that these sorts of savings are possible - it just seems too good to be true, and they are too risk-averse to rejig their organisations to the required extent; they also fear being beholden to a small priesthood of very clever technical people from which there is no way back - eg we can't ship our Clojure codebase back offshore to some commodity programmers because they won't be able to make head or tail of it.
Common Lisp was designed to be a successor of such a Lisp and was supposed to support both complex and/or large applications. Code density can be a bit higher than C++ (say, factor 3) for larger programs.
In the end 80s / early 90s Evans&Sutherland developed a high-end 3d design software in mostly Lisp - the Conceptual Design and Rendering System (CDRS) - many of the cars at that time were designed with it (from Ford, Jaguar, German brands, ...). A designer work environment did cost most than a million USD per seat - including a high-end graphics system from E&S. The 3d software was written in atleast 150kloc C++ and 400kloc Lisp.
Other CAD systems written in Lisp should have been much larger like iCAD or the design system from PTC (years ago to be said at 7MLOC Lisp). iCAD for example was used at Boeing for the development of turbines, wings, internal stuff - it's even said that there was a complete model of some Boeing in iCAD - which used kind of an object-oriented language on top of Lisp for the parametric construction of technical things. Similar applications and languages still exist - for example GenDL: https://gitlab.common-lisp.net/gendl/gendl
There are a bunch of things which makes this possible: Lisp compilers with various compile-time warnings, extensive runtime error handling facilities, object-oriented programming, good development environments, ...
I guess one issue might be that most developers that get introduced to Lisp, do so in primitive ways.
So they never get to learn about those toolings.
Even with Clojure, not everyone is willing to pay for Cursive.
I find the counter-arguments to your discomfort in this thread very strange. Some big problems just require big solutions. A language might indeed be best suited for smaller problems, but that doesn't mean that bigger problems are a "cultural disease". For Clojure in particular, I recall Hickey saying something to the extent that for the problems where Java is successful, Clojure should be successful too. Maybe this rhetoric has changed, but I think it's also related to people's experience that what takes 750k lines of Java takes under 1000 in Clojure, for some problems, so you get this bias that 750k lines of Clojure is "wrong". No, it's just that the equivalent Java would be a factor of 10-100 bigger. If a language can support modularity, it should be able to scale.
More at addressing your discomfort, I'd be curious to know what you mean by "language without any type safety". Dynamic typing isn't the same thing as untyped, and some dynamic languages (like Lisp) support a notion of compile time that lets you have type warnings, optimization, etc. that you might be used to from static compilers... as far as testing goes, the big testing pushes in industry have come from people dealing with giant C++ and Java projects more than the dynamic language crowds, because more energy for testing is needed in general. Whether dynamic languages need more or less of it could be interesting, but because of the variations in what we call "static languages" and what we call "dynamic languages" it might not be useful to compare things so broadly. Instead we could compare language against language, and take into account usual dev practices. Having a REPL to leverage while developing, testing, and debugging changes things in a big way, for instance.
> I’ve felt this pain a few times and the only conclusions I’ve drawn are to use clojure.spec for non-trivial code, especially around domain boundaries, and try even harder to solve very small problems in ways that can compose to solve larger ones.
This seems reasonable to me. A lot of where refactoring gets painful is when you have bits of code being reused across wide swaths of the program. Keeping modules small limits the opportunity for that to happen, and makes the scope of impact for a change easier to conceptualize without leaning on a compiler.
The problem I see there is, it seems to be exceedingly rare that you can get a whole team of developers to be so disciplined - and all it takes is one lapse in discipline for it all to fall apart.