Should function arguments be reassignable or mutable?

综合编程 2017-12-31

Working on a defect in Leaf
I had a question: should function arguments be reassignable within a function? Are they just like local variables, or should they be treated specially? It would solve my problem in the Leaf compiler, but I don’t like making decisions for technical convenience. What is the correct answer?

This is an open question and I’d love to hear your feedback. The article is details and my viewpoint, but I don’t reach a conclusion.

Imperative approach

In the languages rooted in imperative programming
, like C, C++, Java, and C#, we can freely use function arguments as local variables.

int calc( int a, int b ) {
    a += b
    b += a
    return a
}

I admit that I often write code that does this. It’s convenient to reuse an existing variable rather than introducing a new one. I realize it’s also gotten me into trouble before. When a piece of code modifies the arguments, but code later in the function wasn’t expecting that.

int calc( float a, int b ) {
    float result = a;

    if (some_conditon_on(a)) {
        b /= 5;
        result += b;
    }

    if (some_condition_on(b)) {
        result = alt_calc(a,b);
    }

    return result;
}

Though contrived, it shows that a second section of the code in the function may be relying on unmodified arguments. It’s a subtle defect as it requires both conditionals to evaluate to true. Add in more branches that may or may not modify the arguments, and the problem intensifies.

In JavaScript, that situation is worse. If I modify a named argument, it also modifies the arguments
array.

function hidden_arg( name ) {
    name = "weird"
    console.log(arguments[0])
}


hidden_arg("expected")

That writes weird
, not expected
.

Functional approach

If we look to a language like Haskell we see that reassigning variables, in general, is frowned upon (is it even possible?). It’s not something fundamental to a functional programming
though, whether a function a reassigns an argument doesn’t affect the purity of that function.

A function could, however, modify the value of an argument, and that would certainly ruin the immutable requirement.

This got me to thinking that perhaps the requirement should go even further: arguments should also be read-only by default. Consider the below code, where the “values” name is not reassignable (C and C++ are of the few languages where this notation is even possible):

//this prevents reassigning the "values" pointer...
float calc( vector * const values ) {
    values[0] = 1; //...but we can still modify the values
    ...
}

What if the default were also to make everything read-only? (This is the typical C++ syntax for how that is done)

float calc( vector const & values ) {
    values[0] = 1; //error!
    ...
}

This function has a much safer signature. I can call it without worrying that my vector might be accidentally changed on me.

I guess it’s unavoidable for this discussion to get deeper into the difference between a name of a value
.

The default, but not a hard requirement

I’m starting to think that non-reassignable and read-only should be the default. If I want a mutable argument, I can mark it.

float sort( vector mutable & values )

For complex value types that makes a lot of sense. But for service types, like say a file or window handle, it would be inconvenient. At least in Leaf, I have a distinct service
type, which could be mutable by default instead. I don’t like inconsistency, but sometimes it has to be sacrificed for convenience.

Another situation that gives me pause is argument sanitization. For example:

float calc( float a, float b ) {
    if (b < 0) {
        a = -a;
        b = -b;
    }

    ...
}

In this situation, we don’t want the remainder of the function to have access to the original arguments. They’re intentionally hidden. Cleaning arguments may not be common, but I do it often enough that I’d need to have a solution for it. Perhaps hiding the arguments by a local variable of the same name might work.

Your thoughts?

I’m undecided on what the correct solution is. Current languages and best practices don’t appear to give a definite answer yet. This makes it one of those engaging topics in language design.

It’s part of my adventure in writing Leaf. I’d be happy to hear your thoughts on the topic as well.

您可能感兴趣的

Written version of Logical operators in C++ Can we use keywords in place of operators in C++ ? Yes, certainly, we can. The ANSI C++ Standard has proposed keywords for several C++ operat...
NDK概论与入门 android兴起的前几年,是sdk火爆的时代。我们了解,探究,不断的开拓sdk的各种陌生用法,不亦乐乎... android的近几年,是ndk横行的时代。一批人走出去了,一批人还在驻足,茫然四顾... 我想告诉你们,不要眺望了,ndk的时代如果都过去了,我们还不知所以,那就相当无聊了。 ...
Infrequently Asked Questions in comp.lang.c (1999) Meta: The HTML version of this list is now considered the master version; the text version may be updated occasionally, but is now obsolete. Ad...
C语言编程基础及指针学习 C语言是面向过程的,而C++是面向对象的 C和C++的区别: C是一个结构化语言,它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到输出(或实现过程(事务)控制)。 C++,首要考虑的是如何构造一个对象模型,让这个模型能够契合与...
C语言/C++编程中关于时间的函数 一.概念 在C/C++中,通过学习许多C/C++库,你可以有很多操作、使用时间的方法。但在这之前你需要了解一些“时间”和“日期”的概念,主要有以下几个: 1. 协调世界时,又称为世界标准时间,也就是大家所熟知的格林威治标准时间(Greenwich Mean Time,GMT...