5 ms·
Well sure but that's just the danger of duck typing vs explicit interface implementation, something Python already suffers from. Without MyPy, Python will alrea
by clusmore 10y ago
Well sure but that's just the danger of duck typing vs explicit interface implementation, something Python already suffers from. Without MyPy, Python will already let you use this function on all sequences of addable things, not just lists of ints. It would be good if MyPy reflected this as closely as possible, otherwise you're throwing away the advantages of Python's duck-typing.
Edit to add: To be clear, sure the result of calling this on any arbitrary sequence of addables might be semantically nonsense, but that's not the purpose of a type checker. It's only to verify type safety, i.e. you won't get runtime type errors if you give me at least this kind of thing.
- dllthomas 10y agoPart of the issue is the name, which is generic enough that with the generic implementation, it probably also wants a maximally generic type. A more constrained type throws away "the advantages of Python's duck-typing", but it also throws away disadvantages, and that can be precisely what you want.
- clusmore 10y agoYes, this is a good point too which I certainly agree with. Sometimes by using the most general type, you actually permit more than you intended and you might restrict yourself from making refactorings to the function because it would restrict the types.
- dllthomas 10y ago> might be semantically nonsense, but that's not the purpose of a type checker. It's only to verify type safety, i.e. you won't get runtime type errors if you give me at least this kind of thing. We either disagree about the purpose of a type checker, or we disagree about what can constitute a "runtime type error". If a type checker can catch inconsistencies that would lead to my returning "semantic nonsense", at low enough cost, I absolutely want it to. Obviously it won't be able to catch all such inconsistencies in all cases.
- clusmore 10y agoI understand your point, and perhaps we don't actually disagree on this. As an example, in Haskell (which you have shown in another comment that you are familiar with), this function would perhaps best be written as sum_and_stringify :: (Show a, Monoid a, Foldable t) => t a -> String sum_and_stringify = show . foldl mappend mempty In this case there are additional rules (semantics, though not in the language) which we associate with monoids like associativity that don't apply to all "addables" (e.g. strings), that might make us feel more comfortable being so general. I think really it's just that we have more expressive language to describe our expectations. Though there's nothing stopping you from making String an instance of Monoid, or most any type for that matter, and producing nonsense. It's not a type error, so I say it's (in this case) not the job of the type system. But there are more expressive type systems that could let you guarantee these additional properties like associativity and turn nonsense into type errors, and I for one am all for that. In the case of Python, though, coming from no static type system or verification at all, I would only ask of this tool to turn runtime checks (type errors caused by arguments lacking __iter__ or __add__) into static checks. Anything on top of that would be fantastic, I agree with you on that, but I wouldn't ask for or expect it. Finally, thanks for all your comments - I appreciate the discussion.
- dllthomas 10y ago> Anything on top of that would be fantastic, I agree with you on that, but I wouldn't ask for or expect it. Ah, we probably do agree more than it seemed. "The MyPy team has done its job even if that's not possible/reasonable" is a statement I'd endorse - especially in the short-to-medium term. Discussing the Haskell... String concatenation actually does associate; in fact String does form a Monoid over concatenation, and has an instance visible from Prelude - specifically the list instance, because Haskell strings are just `[Char]`. The only really "nonsense" thing about using a string here that I see is that we wind up with quotes and escaping in the result when we `show` the string. If the purpose of this is "gather results and format for logging", that might be fine. Numeric types don't have an instance of Monoid, because there are two to pick. You can pick them with newtypes, but here that's a bit awkward (`sum_and_stringify . map Sum`? feels redundant), and then the Show will give you something like "Sum { getSum = 3 }", which is probably not what you want either... Using Num rather than Monoid probably has better ergonomics, and may be closer to intent anyway. That is to say: sum_and_stringify :: (Show a, Num a, Foldable t) => t a -> String sum_and_stringify = show . foldl (+) 0 Of course, we have a `sum` defined (basically, perhaps literally) `foldl (+) 0` so why even pull this out into a function? `(show . sum)` is actually substantially shorter than `sum_and_stringify` and no less clear. I think the biggest issue here is that the example is so devoid of context (probably necessarily) that it's hard to say what good style would be. In actual code, a function with that name should probably have a very general signature - but we likely shouldn't be writing a function with that name. Why are we wanting to "sum and stringify" as one action in this way? Is that something that might change? Will the uses want to change together or are we just Huffman coding? With a domain-relevant name, we'd have a sense of what ways it makes sense to restrict the types. > Finally, thanks for all your comments - I appreciate the discussion. Likewise!