30
Nevoic
6y

Dynamically typed languages are barbaric to me.

It's pretty much universally understood that programmers program with types in mind (if you have a method that takes a name, it's a string. You don't want a name that's an integer).

Even it you don't like the verbosity of type annotations, that's fine. It adds maybe seconds of time to type, which is neglible in my opinion, but it's a discussion to be had.

If that's the case, use Crystal. It's statically typed, and no type annotations are required (it looks nearly identical to Ruby).

So many errors are fixed by static typing and compilers. I know a person who migrated most of the Python std library to Haskell and found typing errors in it. *In their standard library*. If the developers of Python can't be trusted to avoid simple typing errors with all their unit tests, how can anyone?

Plus, even if unit testing universally guarded against typing errors, why would you prefer that? It takes far less time to add a type annotation (and even less time to write nothing in Crystal), and you get the benefit of knowing types at compile time.

I've had some super weird type experiences in Ruby. You can mock out the return of the type check to be what you want. I've been unit testing in Ruby before, tried mocking a method on a type, didn't work as I expected. Checked the type, it lines up.

Turns out, nested away in some obscure place was a factory that was generating types and masking them as different types because we figured "since it responds to all the same methods, it's practically the same type right?", but not in the unit test. Took 45 minutes on my time when it could've taken ~0 seconds in a statically typed language.

Comments
  • 2
    @zlice even if I and everyone on my team agreed to do that (they wouldn't), it wouldn't have the same benefits.

    The non-existent compiler doesn't know what type is expected, so it can't suggest what methods to call on it. In Kotlin/Java/Swift/Rust etc. the IDE/text editor knows *exactly* what set of methods can definitely be called on what object because of the compiler. Literally no typing errors or method doesn't exist errors will make it to runtime.
  • 1
    Preach it man!!
  • 0
    I guess its because of compilation as an extra step. Although this is mostly for the languages I know. Most dynamically typed languages are interpreted so working with them is just faster.
  • 1
    @SanitizedOutput is this referencing type coercion? Implicit type coercion is nightmarish IMO. It breaks the transitive property.
    I.E:

    0 == "" is true
    0 == "0" is true
    "0" == "" is false.
    (In JS)
  • 0
    @xecute I'd argue that working with a compiled language is "just faster" because you get immediate feedback for a lot of basic typing/syntatical errors instantly, and you don't need some hacky linter to try to verify syntax or guess whether or not a module will work (it's right about half the time).

    The "extra step" mentality is honestly entirely psychological. I'm not even sure it adds to the runtime of the app anymore. Actually a good compiler can reduce the runtime of an app through optimizations (look into Java streams, this is an example of that)
  • 0
    @Nevoic Interesting argument. I wonder if there is some way to test this. I do know about Java streams. But I havent benchmarked them against any other form of io so cant compare. Also JS example resonates so much with me. I hate that stuff.
  • 0
    @xecute the thing I'm referencing about them is they won't execute useless operations, or they'll condense multiple operations into one to reduce redundancy.
  • 0
    Iirc
  • 0
    @SanitizedOutput you can overload operators in modern statically typed languages (a lot of people view Java as modern because in school they're taught Java & C as the statically typed languages).

    But Kotlin, Swift, Rust and Crystal are all more modern.

    I'll use Kotlin as an example because it's the most similar to Java.

    You can have default paremeters in Kotlin.

    And to have dynamically typed parameters and return types as well as a paremeter with a default type, this is all that's necessary:

    fun <T, Q> methodName(t: T? = null): Q {
    // Write code that returns Q
    }

    Also explicit null types are a blessing. I can't believe languages still allow all types to be null/nil/undefined, it's just unexpected behavior that is super easily protected against (in a statically typed compiled language).
  • 0
    @SanitizedOutput to be fair you can't call any method you want on T or Q, you'd need to make them extend known classes or interfaces, or just have the type as such.

    I don't view that as an advantage for dynamically typed languages though because that "feature" allows for runtime method not found errors, as well as some other funky unnecessary stuff.
  • 0
    Also if the character count is of concern, then Crystal is the way to go (looking identical to Ruby but still providing static types, often union types in the situations where we'd normal use interfaces).
Add Comment