The amount of cruft I had to type in C# just to get shit done... It's all implicit in ruby thank god for that.
I never EVER have to check the type of a variable at runtime. I always know its type just by looking at its name. Is it enforced in ruby? Of course not. Ruby assumes I'm an adult and I know that I'm doing.
At 40 years old, I've seen enough of my own incompetence that I'll gladly accept things that can mitigate it. As for excellence, I suppose static typing would have prevented a handful of clever hacks that I did in Python and Lua when I was in my 20s, 12+ years ago. Truthfully though, my memory of that period has faded enough that I'm not sure, and I doubt that any of those hacks were crucial for the products that I was developing at that time. Yes, a type system as primitive as Java's at that time would have felt like a straitjacket. The same might have also been true for C#. But modern static type systems are much more flexible, and I don't think I've rejected a language based on its static type system in the past several years. (I've recently done a project in Elixir, but that was despite its dynamic typing, not because of it.)
TIL taking notes of things you want to be reminded of in the future is for children and the incompetent.
But in C# for example, if the system was not designed with dependency injection and everything being an interface it's very hard to build a test harness since you can't mock anything. Which means everything has to be tested manually. (I haven't done any C# in a long time, maybe it's not the case anymore)
So you have to create an interface and classes for every implementations for every type in the system just so I can change its type dynamically. By the time you're done with all the cruft, you forgot what you were about to code.
I'm infinitely more productive in Ruby compared to C#. But I can understand dynamic languages not being welcoming to juniors, since they can code themselves into pitfalls that will bite them later.
Do you ever feel the names are getting too verbose and it would be great to have tooling that would allow you to get that information on mouse-over instead of having it make your lines almost unreadable?
I mean, there's a reason mathematics have decided to keep variable names short instead of having the names contain all the context.
There is zero real world evidence for that statement. The smartest developers I have ever worked with love types. The not-so-smart ones couldn’t figure out how to use types well and their code was a buggy mess. Not evidence of anything of course but certainly a sample point.
> I never EVER have to check the type of a variable at runtime.
> I always know its type just by looking at its name
I guess you've only ever written web backends and menial things like that?
(although, IMO, I think purity makes a very large impact here too)
Programmers spend more time reading code then writing it. So I personally prefer the devs in the team will spend more time typing the code or use a bit more brain energy to think about types so later we can all read the code and understand it and edit faster.
Dynamic works great for write-only scripts.
Secondly, since I’ve learned statically typed languages, I already have a mental model for how they make you structure your code, except dynamically typed languages make patterns easy that would require something like dependent types to check (see how complicated Typescript is, because it has to be able to model JS idioms). My experience is that a lot of the value of static types isn’t in the checking but in the modeling aspect: if you follow the general patterns you’d use in Haskell (represent algorithms like “apply a function to each member of the list” as functions), you reduce the amount of thought it takes to see the program is correct by splitting it up. For example, if I have this pattern in my imperative codebase:
let result = []
for (let idx = 0; idx <= input.length; idx++) {
result.push(input[idx]+1);
}
return result
I have at least three things mixed up together: accessing each member of a list (and there's an easy to miss off-by-one error in this implementation), transforming that member and building up a result. If I translate this to a functional style, it's easier to see that the implementation is correct: const inc = v => v+1
. . .
return list.map(inc)
Looking at this code, I can break down correctness into three questions: is list.map implemented correctly? is inc (the transformation) implemented correctly? And, assuming both are correct, are these two functions combined in the correct way? Types definitely can help here but my experience is that 90% of the benefit isn't the _checking_, it's the code structure you end up with as a result.[1]Now, if this is true, why do I prefer dynamically typed languages? Well, it comes down to two things: I find the "live programming" model of CL/Clojure more productive and roughly equal to types when it comes to checking correctness (and I don't think it's just me, I've seen various papers, etc. that claim Haskell and Clojure have roughly equal defect rates); and, I find the patterns I like in CL/Clojure/Javascript require much more sophisticated type checkers to actually validate, and such type-checkers have a huge up-front learning cost and still add a lot of boilerplate that exists mainly to convince the type-checker that you know what you're doing.
Finally, in a language with macros, you can roll your own static guarantees: one project I worked on was doing a bunch of calculations inside a database. We hit an edge case where the DB's idea of a week didn't match our requirements. As a result, I wrote a code generator that generated Clojure functions and DB queries simultaneously. In this situation, if you assume the code generator is correct, you have a compile-time guarantee that the Clojure versions of the queries are equivalent to the calculations being done inside the DB.
[1]: This page surveys a bunch of studies on the question of dynamic v. static types and finds the evidence in favor of static types to be surprisingly small https://danluu.com/empirical-pl/
Most of the studies seem to be rather poor though, so difficult to draw any solid conclusions from them. Almost all seem to drown in noise, or have flawed setups.
From personal experience, with a static type language I can jump into an unknown codebase and make non-trivial modifications much, much faster than if it's a dynamic type language codebase.
I've wasted soooo many hours doing print(dir(x)) in Python it's far beyond funny.
On the flip side, over the years I've helped countless people with their C/C++/Delphi code in minutes, frequently using libraries and API's I've never seen before.
But I think it is possible to have all 3, it just doesn't exist in any popular language that I am aware of.