7 ms·
The greatest sinners are methods that both mutate and return their result. It's possible for people to use some of these for years and not realize they're mutat
by ris 8y ago
The greatest sinners are methods that both mutate and return their result. It's possible for people to use some of these for years and not realize they're mutating. This is something python has largely got right.
- baby 8y agoThat's OpenSSL for you.
- jacobush 8y agoWhich is interesting given how easy it is to mutate in Python.
- hellofunk 8y agoEasy, yes. And more importantly, explicit, unlike the JS examples in this article.
- nerdponx 8y agoThis is only because good precedents were set by the built-in functions. Mutating functions return None, and only non-mutating functions return something non-trivial. It's purely convention, but therein lies the power of the core language setting a good example.
- drinchev 8y agoI did that mistake with `sort`. There was a nasty bug that I tried to fix, when I realised that `sort` actually mutates the array and returns it as well. That was pretty good lesson to learn.
- aidos 8y agolist.sort returns None in python - and I’m sure it’s been that way for a long time. sorted is the function you mostly want as it returns a new list each time.
- coldtea 8y agoIt would be even better if it was a linting/compile or at least a runtime error to assign a variable to the result of one of those functions. Python let's you do it, and you get "None" as the result, which makes such mistakes slower to discover. This would require some way to mark a function as returning or void.
- nothrabannosir 8y agoA clean solution would be arity of return values, optionally with a ≥ constraint on the original number of returned values vs bound variable names. E.g. a function returning two values can be bound to zero, one, or two variables, but not three. A function returning 1 can be bound to only 1 or none, and if you return no values, you cannot be bound to anything. Aside: it is a common misconception that Python has multiple return values: it does not. It can return a tuple, which can be automatically destructured into multiple values: a, b = (1, 2) # this could have been a function and it has syntactic sugar for tuples which allows leaving off the parantheses: >>> 1, 2 (1, 2) Which, combined, makes it look like multiple return values: def foo(): return 1, 2 but, it really isn't: >>> foo() (1, 2) >>> type(foo()) <type 'tuple'> so this would be hard to achieve in Python. :(
- coldtea 8y agoWell, couldn't it be like "no return inside the function body" --> no LHS assignment allowed?
- nothrabannosir 8y agoSure :) but it would require a deep change in Python language semantics, without which this isn't possible. You'd have to carry that information in the AST somehow. From a call site, a function object is just a an object with certain properties. "Did it have a return or not?" is not information which exists on that object. Only the return value is (when you call it), and if it is None, you don't know if that came from "return None" or from the default behaviour. Python could enrich the function object to include that information, and it would essentially be "supporting 0 or 1 return values", instead of "n return values". But adding that no-assignment rule would be backwards incompatible. At that point, might as well go all the way and support any number return values :)
- goldenkey 8y agoRealize in C and JS and most languages, the standard equality '=' operator, does exactly this. For example, console.log(2*(a = 10)) would print 20 while setting a to 10
- tytytytytytytyt 8y agoMost languages?
- reificator 8y ago> Realize in C and JS and most languages, the standard equality '=' operator, does exactly this. > For example, console.log(2(a = 10)) would print 20 while setting a to 10* `=` is the assignment operator, not the equality operator. The equality operator is `==` in C and `===` in JS.
- goldenkey 8y agoYou are correct, my mistake.
- marcosdumay 8y agoMost languages do not have that feature. Even the C-like languages usually have some comment by their designers claiming that C made mistake there and they are not adding it to their language.
- klibertp 8y agoIn C and JS assignment is an expression which returns the value being assigned. This makes the semantics of things like `a = b = 10` trivial. In Pony, though, this is averted, as the assignment there returns the old value of a variable being assigned: var a = 10 var b: Int b = a = 20 a == 20 and b == 10 // very true It's a very interesting approach I wish I saw explored more often.
- quotemstr 8y agoSo you hate all builder-object methods that return the builder for further building? Ok.
- jakelazaroff 8y agoThis isn't the builder pattern, since these methods directly mutate the final result rather than a distinct builder object. Also, it's perfectly possible to have a non-mutatative builder that returns an entirely new builder instance with the given configuration at each step.
- ris 8y agoYes. For the reason stated and also because they imply an easy/cheap/straightforward way of taking a copy of one of those objects that actually doesn't work. Does the caller of your function expect you to take that object it passed to it as "configuration" and mutate it?
- paulddraper 8y agoIn other words, the jQuery convention.
- masklinn 8y ago> The greatest sinners are methods that both mutate and return their result. Go's `append` builtin is guilty of exactly that sin. And worse, it does not always mutate its parameter in-place, so things get hellish if you start aliasing/shallow-copying slices because depending on the size of the underlying array, appends could overwrite the other slice's data or make the two slices diverge entirely.
- Sir_Cmpwn 8y agoappend has a good reason for that, it can potentially reallocate the slice and return a new pointer. This is an expression of the underlying truth of the implementation that they chose not to abstract over, a decision I personally appreciate.
- TheCoelacanth 8y agoThat's just a language that is insufficiently powerful to express the underlying truth. In C++ or Rust, the same construct could be expressed without loss of efficiency and without the leaky abstraction.
- Sir_Cmpwn 8y agoCan you elaborate?
- TheCycoONE 8y agoNot the original poster, but in C++ you would be using a container, likely a vector so the internal data could move, but because the container is updated the caller can be unaware. The abstraction leaks a bit. The result of a data() or c_str() call cannot be trusted after the vector is manipulated. When passing a vector by reference to another function you effectively get a double pointer (abstracted behind a reference and a container) so the inner one can be moved. I'm not familiar with go, but if it is like C in this respect append would have been clearer with a double pointer and no return value.
- bastawhiz 8y agoAs someone who writes JavaScript full time, this is my biggest gripe with the language. Folks sorting arrays inline without first copying them is one of the things that I pretty consistently call out in code reviews.
- TimJYoung 8y agoI'm sure that those cases are where you didn't want the mutation, but there are certainly performance and efficiency concerns that are directly satisfied by having the sorting operate on the original array. This is especially so when considering that such operations can be occurring in the UI thread (and not a web worker) where excessive computation can interfere with the responsiveness of the application.