I vehemently disagree with dynamically typed being a winning point of Lisp. SBCL's strong support for type checking is the main reason I was drawn from Scheme to CL, and Coalton (https://github.com/coalton-lang/coalton) is one of the most interesting Lisp projects I have encountered.
Type checking can remove an entire class of bugs from even being a consideration. Yes, it could be argued that type mismatches are a trivial class of bug, and yes, proper testing should catch any issues... but catching problems before you go to testing can save you precious seconds, especially when coding in the typical interactive style of Lisp. Lisp lets you code at amazingly high velocity, good support for type checking helps increase that velocity even further.
I think it would help the conversation along a bit if those who are against static type checking could articulate exactly how type checking gets in the way of writing correct programs.
Isn't making sure the types flowing in and out of your functions match, at some point, something you will eventually need to do anyway? Or are we trying to say the type system doesn't allow for perfectly safe things that should be allowed?
I'm not sure I understand it.
Then again, I'm also the person who wonders if the restrictions Rust puts on "valid" code aren't also too restrictive, so maybe we all exist on a gradient?
In almost every instance I see a function in a statically typed language that should return a sum type the developers inevitably collapse that into a single type eg. we're going to return the number of results and -1 is a sentinel value that means something special. This is a fundamental type error. In a dynamic language you tend to not need to embed a concept or value in a domain it doesn't exist. If later I'm adding results together I'm going to get an error when I try to add the Clojure keyword :something-special to a number, while in the static language you may have just folded the special -1 into a sum because you forgot it was unique.
Yeah, I've encountered real-life code that had such issues (was even running in production without anyone noticing). Type checking won't prevent every type of bug, but that kind of problem (returning a special value and forgetting that it should be handled differently) can occur in a dynamic language as well.
Just because keywords and symbols exist doesn't mean the hypothetical programmer who would return -1 as a special value in a static language will not do so in a dynamic language. But in the statical language when the sum type is used it will prevent people from passing the result into an arithmetic function when more code is added in the future (in an ideal world tests would catch it, but in an ideal world the sum type would have been used from the very beginning).
There are trade-offs between static and dynamic typing, and while dynamic typing allows us to write code more quickly, things balance out when we include the time lost by type matching errors that static type would protect us from.
It's like the C vs Rust discussion but with less potential for leaking important customer data all over the web.
That is indeed an argument against static typing and in favour of dynamic typing, although the demerits can be mitigated through some extent through a combination of organisation and tooling.
For example, if our customer IDs used to simply be increasing integers but then we decide to change to a UUID variant it would be a pain if I had something like:
And then had a bunch of functions calling out to that which all require integers and I had to change all the declarations from integer to UUID. However, I could save myself some future headaches by doing something like:
(deftype customer-id nil 'integer)
And using the customer-id type everywhere. Of course, this is a very trivial example and a real problem would be harder to manage. That said, with optional type checking like SBCL has it is entirely possible to fly by the seat of your pants until things start to take shape.
Not that it matters too much if you go with the tried and true method of tossing out the first two prototypes, but that's another discussion.
Type checking can remove an entire class of bugs from even being a consideration. Yes, it could be argued that type mismatches are a trivial class of bug, and yes, proper testing should catch any issues... but catching problems before you go to testing can save you precious seconds, especially when coding in the typical interactive style of Lisp. Lisp lets you code at amazingly high velocity, good support for type checking helps increase that velocity even further.