| > See, I feel the exact opposite: I use Iterable only if I have a reason to force consumers to use Iterable. A broad argument type doesn’t force consumers not to use a narrower type. (It forces the implementer of the function to not rely on additional features of the narrower type, but if I am writing the function, I can be certain whether or not that is acceptable.) Meanwhile, using a narrower type than needed for an argument does impose additional, unnecessary constraints on the consumer. > When you're marking argument as Iterable, how confident do you feel that you will never query collection size or access it by index? Absolute certainty, since I know what the function does and what I need to do it. > I understand the desire to limit the interface and YAGNI, but since lists are more familiar and ubiquitous, using Iterable feels more complicated and unnecessarily pedantic. Since all lists are Iterables but not all Iterables are lists, Iterables are necessarily more ubiquitous than lists. |
Yeah, that's what I meant by being pedantic :)
Here's a question: you receive a JSON payload that contains a list. You will then pass this list to two functions, one of them only iterates, another one uses list interface (let's say checks length among other things). Should you mark the argument as a list, or as an Iterable in the first function?
Solely from the code perspective, it's definitely an Iterable. But in my mental model it still remains a list. I don't like it when code deviates from my mental model. Forcibly treating it as an Iterable only makes it more complicated, while not giving anything in return.
Sure, you could say that callee should not have expectations of the caller, but what if those functions are already coupled? They are in the same module, and argument names clearly denote a collection. The fact that in certain scenarios it is "technically Iterable" serves nothing but pedantic value.