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.
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.
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.
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.
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. :(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 :)
That said, a "pure" modifier that ensure that no side-effect code is allowed inside a function would be good indeed.
(There could be purity-levels as well, e.g. absolutely pure (doesn't even log or write something to a file or use a random number generator inside etc) vs "doesn't mutate program state" (which is a different kind of the same purity concept I think).
[1] Not really, though. There's the process dictionary, and there's no way to statically or dynamically mark a function as not using them. The use of it, however, is discouraged to the point that most popular Erlang books only mention it in passing close to the end, or not at all.
For example, console.log(2*(a = 10)) would print 20 while setting a to 10
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.> 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.
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.
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.