logoalt Hacker News

ARandumGuylast Monday at 4:16 PM1 replyview on HN

I don't find that dynamic typing reduces the number of places I need to update stuff. It just changes when the error occurs.

If I change the shape of some data (such as renaming object properties), I'll need to update all the code that used that data, regardless of the type system. Static typing just ensures that I catch those cases at compile time, not runtime.


Replies

embedding-shapelast Monday at 4:50 PM

Just one simple and contrived example I could come up with the five minutes I had available:

JavaScript:

  // rename host -> hostname, update ONE place
  function connect(opts) {
    const host = opts.hostname ?? opts.host; // compat shim
    return `tcp://${host}:${opts.port}`;
  }

  // old call sites keep working
  connect({ host: "db", port: 5432 });
  connect({ host: "cache", port: 6379 });

  // new call sites also work
  connect({ hostname: "db", port: 5432 });
TypeScript:

  // same compat goal, but types force propagation unless you widen them

  type Opts = { port: number } & ({ host: string } | { hostname: string });

  function connect(opts: Opts) {
    const host = "hostname" in opts ? opts.hostname : opts.host;
    return `tcp://${host}:${opts.port}`;
  }

  // If instead you "just rename" the type to {hostname; port},
  // EVERY call site using {host; port} becomes a compile error.
Again, this is just a simple example. But multiply 100x + way messier codebases where everything are static types and intrinsically linked with each other, and every change becomes "change -> compile and see next spot to change -> change" until you've worked through 10s of files, instead of just changing it in one place.

Personally, I prefer to spend the extra time I get from dynamic languages to write proper unit tests that can actually ensure the absence of specific logic bugs, rather than further ossifying the architecture with static types while changes are still ongoing.

show 1 reply