Should function arguments be reassignable or mutable?
mortoray.com
mortoray.com
Where mutable variables are needed, I prefer to hide that logic inside a method that exists solely to do that dirty business on an abstract level, not specific to my business logic. That let's me test the 'dirty' mutable method in an isolated place and removes a whole class of mutability-related bugs from my business logic code.
Working on Java, this used to be nigh-impossible, but the Java 8 additions have truly made my life easier.
Firstly, "Immutable by default" is recognised as a good design feature now for modern languages. But there is plenty of code in existing languages like Java, JavaScript, C, C# etc. which does not follow this rule.
Secondly, the "principle of least surprise" suggests that function arguments should work like local vars in that language. So if local vars are immutable by default, then arguments should be as well. Or if local vars can be optionally declared as immutable, then function arguments should be as well.
I can't really say what's the best for every/any language. It's not one size fit all. There are always design tradeoffs and differing schools of thought. But these days it looks a lot like "immutable by default, unless there's a good reason otherwise" is a good starting point.
Example of immutable assignment: int n = 2
Example of reference to mutable value: ref int n = (heap int := 2)
However, in order to make Algol-68 code look more similar to Algol-60, they introduced backwards-compatible shorthands, so for example the declaration int n; was actually a shorthand for ref int n = (loc int);
This shorthand magic probably contributed to why Algol-68 syntax was perceived to be difficult.
Footnote: loc and heap were the two memory allocation operators available in Algol-68.
Here is an example of using a recursive function in Erlang to sum up a factorial.
fac(1) ->
1;
fac(N) ->
N * fac(N - 1).And yes, passing a reference or pointer by value enables you to have effects that are visible outside the function, but if you copied the reference or pointer value to a local variable, you still could have effects visible outside the function, so that isn't a question of how arguments should be treated; it's a general question of how functions should behave.
This is perfectly valid Haskell:
main = do
let a = 7
print a
let a = 9
print a
but you'll get a warning for it: tmp.hs:4:7: warning: This binding for ‘a’ shadows the existing binding bound at tmp.hs:2:7The difference is that when a is reassigned the 7 is lost, while it should be still around when shadowed. I don‘t know of a way to access the 7 in this example however.
fn foo(a):
a = a |> trim |> (default "chocolate")
mix a "milk"
Well, in this contrived example, I wouldn't reassign a, just tack on `|> (mix "milk")` but in a complex example where you're reusing `a` in several places, it can make sense and is nicer than having to declare a bunch of variables just to hold the sanitized values of arguments. int add(const int a, const int b);