Hacker News new | ask | show | jobs
by IshKebab 7 days ago
Nobody writes dynamically typed functions that can be called with any possible type.

It's not about that at all. Static types give you errors reliably at compile time instead of randomly at runtime, better documentation of what the code expects (people writing dynamically typed languages eventually resort to type comments), working IDE support, reliable refactoring and better code, all of which results in faster development.

The cost is a more complex language, occasionally difficult-to-write types, and very occasionally impossible-to-write types. But those are very very minor in comparison to the pros.

1 comments

> (people writing dynamically typed languages eventually resort to type comments)

This has never been an issue in Elixir, because instead of a comment, you'd just improve the pattern matching in the function definition.

    def blah(%{students: [%{firstname: firstname, lastname: lastname}|[]], count: cnt}) when is_int(cnt):
      fullname = firstname <> lastname
Is a valid function declaration, which specifies that blah takes a dictionary that contains at least 2 keys, :count, who's value is an integer, and :students, who's value is a length-1 list who's first element is a dictionary that contains the keys :firstname and :lastname
is_int is doing the work of a type checker?

I mean I'm not an Elixer guy (not at all) so it's a bit opaque to me how that's not an enforcement of specific types (albeit the firstname, lastname can be anything?)

Once we get into the function itself, if two incompatible types, say a string and a filehandle.. (just random attempt, you may, if you choose, point to why the two types must have some alignment), what happens - the function dies?

That's my core issue with dynamic typing, you might argue that the function's explosion has a restricted blast radius, but that's not the point, the point is that the function was called, and was unable to compute whatever, and that's a bug.

In elixir, processes (these are Erlang "processes", not OS processes) are designed to fail when they encounter errors, and the supervising process will restart it according to various rules you can configure. This isn't dissimilar to how kubernetes restarts jobs when they fail, but at a more fine-grained level.

It's actually intended that your function doesn't try too hard to validate it's input types, and just that it will malfunction and crash when it gets something it doesn't know how to handle, because the supervision tree will rescue it and your program can continue, albeit without whatever input caused the malfunction.

This obviously isn't ideal for many types of software, but for complex backend servers and other long-running operations it works really well.

Of course this is not a guarantee to eliminate bugs, but it's a factor that reduces them more than you'd think when coming from a different programming paradigm.

I mean - you're arguing there about a small blast radius, and I genuinely appreciate that not taking down the whole system when a bug occurs is incredibly valueable.

But, the discussion is about dynamic typing, and the fact that the function is in a state where it cannot compute because of bad input.

This might as well be a Null Pointer Exception - the function has encountered bad input, and crashes.

The argument is, that a MUCH stronger position is for all callers to be forced to provide input that cannot get the function into such a state - and a major subset of the set of all inputs possible can be eliminated with static or strict typing (I know the nomenclature gets a bit hazy here, but I hope that my intended meaning is clear)

If the callers know in advance that they cannot provide input shaped in ways that the function will not handle, then they will not call the function in those ways.

And that forces the call chain to be sure about the data transferred from top to bottom.