> Why on earth use a dynamic language like Ruby or Python and then try to bolt types on top.
To answer this (as someone who basically only ever writes in Python):
There are a few cases where it's really nice to be able to add type annotations to methods or functions. The most obvious example is API calls; it's nice to be able to say "this needs to be a list, give me a list", and not have to do
if not isinstance(var, list):
var = list(var)
or
if not isinstance(var, list):
raise ValueError("I know I didn't tell you I needed specifically a list, but I need specifically a list in this case")
Over and over and over again all over your module. Look, give me a list, I need a list. I need the APIs that list has, I need the interface it uses. I don't want a generator that I'm going to be iterating over forever, I don't want a string that's going to get split into individual characters.
Duck typing is all well and good, but just because strings, lists, sets, and os.walk are iterable doesn't mean I'm able or willing to handle those.
It can also help a lot in IDEs; for example, if I type-annotate a method to accept "name" as a Str, then my editor can assume that "name" is a string, even without any other evidence to that being the case. Likewise for things like warning about return types.
Lastly, it lets you do automated testing as well. Hey, you're passing a FooNode to this function, but that function accepts a list. I know this because NodeCollection.find() returns a FooNode. Makes it easy for the dev to look at the report and think "Oh, I meant to use NodeCollection.findall(), oops!"
I certainly don't want a statically typed language, but there are a lot of cases where my internal logic is fixed and I don't want my method to have to know how to deal with int, str, none, bytes, etc. Type annotations can solve this problem for me and for other people using my code.
I just worry it's going to be abused, though. E.g. I've worked with more than one Ruby code base where someone did a kind_of? check and threw exceptions if it didn't get what it wanted even though the actual type required was anything that implemented a given method in a reasonable way for no good reason.
I hope people keep the type annotations sparse, and allow the tools to infer it unless they're prepared to link long and hard about the minimal restrictions that are reasonable.
I think your own example proves that your concern is moot. If people are going to do stupid stuff, they're going to do it with whatever tools are available to them. Your kind_of misbehavior already happens without type annotations, but now it can be clear to you beforehand what's going to happen.
I hope you're right. I just fear that making it external to the code will make it easier for people to ship overly restrictive type signatures without thinking. Though hopefully the tools like sorbet will make it easy to override, in which case it might well improve things (if I "only" need to override the type signatures instead of having to monkey patch or fork code)
To answer this (as someone who basically only ever writes in Python):
There are a few cases where it's really nice to be able to add type annotations to methods or functions. The most obvious example is API calls; it's nice to be able to say "this needs to be a list, give me a list", and not have to do
if not isinstance(var, list): var = list(var)
or
if not isinstance(var, list): raise ValueError("I know I didn't tell you I needed specifically a list, but I need specifically a list in this case")
Over and over and over again all over your module. Look, give me a list, I need a list. I need the APIs that list has, I need the interface it uses. I don't want a generator that I'm going to be iterating over forever, I don't want a string that's going to get split into individual characters.
Duck typing is all well and good, but just because strings, lists, sets, and os.walk are iterable doesn't mean I'm able or willing to handle those.
It can also help a lot in IDEs; for example, if I type-annotate a method to accept "name" as a Str, then my editor can assume that "name" is a string, even without any other evidence to that being the case. Likewise for things like warning about return types.
Lastly, it lets you do automated testing as well. Hey, you're passing a FooNode to this function, but that function accepts a list. I know this because NodeCollection.find() returns a FooNode. Makes it easy for the dev to look at the report and think "Oh, I meant to use NodeCollection.findall(), oops!"
I certainly don't want a statically typed language, but there are a lot of cases where my internal logic is fixed and I don't want my method to have to know how to deal with int, str, none, bytes, etc. Type annotations can solve this problem for me and for other people using my code.