Idea for reducing novice confusion related to function parameters.

Ff1a7adf06dc94b6271e4e632ce4aac0
0
0x4A45535345 101 Feb 02, 2010 at 21:27

When someone learns to program for the very first time it seems like one of the most common confusions is that of understanding whether or not a particular parameter in a function is passed by value, by pointer, or by reference.

For example, it doesn’t seem uncommon for a new programmer to believe that the following function might modify the passed variable itself (rather than a copy):

void add_five( int val ) {
   val += 5
}

This code of course does nothing useful in C/C++, because val is just a copy.

One question we might ask then, is what is the most intuitive interpretation of this? If you take a random person off the street who hasn’t ever been exposed to programming, would they say that it’s more intuitive to assume it won’t modify the original or that it will modify the original?

What was your fist intuition when you started programming? I’m curious what it was, if you can remember. I could see people arguing for either of the sides.

So, theoretically if we were designing a language with one of the goals being to eliminate this confusion what might we do? The confusion often comes from the inconsistency that in many languages primitives are passed by value and objects are passed by pointer or reference (and therefore appear to behave differently). Of course, under the hood, a reference is largely just a pointer passed by value and dereferenced automatically, but the way humans think about it still comes into play.

To make it consistent, we could perhaps enforce that all function parameters appear as if they are either all passed by value or all passed by reference. Also, suppose we don’t want the programmer to have to deal with pointers directly. In that case, we can’t really choose to pass them all by value, because then no function could ever modify it’s parameters (remember, I said assume no pointer values). Therefore perhaps we could make everything (including primitives) appear to be passed by reference (with some hidden exceptions).

We could state that all parameters could only be declared syntactically as either being constant references or non-constant references in the syntax of the language. In the case of the constant reference parameters we could have the compiler do some “smoke and mirrors” and compile literal primitive values and constant variables as passed by value (under the hood). If the programmer used it as a reference in the function, it could compile as a reference instead. The idea would be to make it so the programmer didn’t have to concern themselves with whether a parameter is more efficient as a value or as a reference, because the compiler would figure it out on its own for those special cases. This is one of the kind of things that a programmer shouldn’t have to waste time worrying about, in my opinion.

With these function parameter limitations we would not be able to ever use the parameter as a temporary variable for performing calculations within the function, but this effect is both intentional and fairly beneficial. By forcing the programmer to not be allowed to use the parameters as temporary variables, we also force the programer to retain all initial state information of the function call (except where we intentionally modify referenced data of course). To use temporary variables, a function local variable would have to be declared explicitly, thereby making its use much more obvious.

For example:

// parameters are: constant and reference to int, respectively
int do_something( const int val1, int val2 ) {  
   int tmp_val1 = val1;   //use of temporary variable
      ...
   val2 = 0;   //changes the value of the caller's variable
}

Also, it seems wise to me to require that all function calls explicitly acknowledge the possibility of a variable being changed during the function call, to make reading the source easier and more traceable.

For example, we might require that a symbol be placed before every non-constant reference parameter in a function call in order to indicate this. The reason for doing this is to make it very easy for a programmer reading the code to pick out which parameters might be changing during any and all function calls, hence resulting in better and more self-aware code practices and maintainability.

C/C++ programs can partly indicate the possibility of values changing, by consistently using pointers instead of references thereby forcing the user to use the address-of operator (&) where the variable is passed. However, if the variable being passed is already the pointer of this address, then the & will not appear in the function call and it will hence no longer make it as easy to realize that a variable could be being modified there.

Instead, with parameters restricted to references, we would require a “change acknowledgment” symbol that bears a resemblance to the & usage

For example, if “$” was our symbol for indicating the possibility of variable change due to a function call, then a call to a swap function might look like this:

int x;
int y;

swap( $x, $y );   //explicitly indicates the values x and y might change in the function call

rather than using pointer syntax like so:

int x_val;
int y_val;

int* x = &x_val;
int* y = &y_val;

swap( x, y );   //does not explicitly indicate the possibility of change, although we know it will 

Together, these two conventions (all parameters appear to act like references, and all function calls must explicitly indicate the possibility of variable change) could, I think, make for a more consistent and less confusing function system for new programmers.

Of course, not having access to pointers is perhaps bad for doing systems programming, like making an operating system. But, you could always include direct pointer access as a feature that can be enabled optionally, for this reason.

What do you think?

Also, are there any languages that already do any of this?

9 Replies

Please log in or register to post a reply.

A8433b04cb41dd57113740b779f61acb
0
Reedbeta 167 Feb 02, 2010 at 21:58

The C# ‘ref’ and ‘out’ keywords do something a bit like what you’re suggesting. You can define a function taking a parameter of type ‘ref int’; then when you call it, you must prepend ‘ref’ to the name of the variable. So the ‘ref’ does something like what & does in C, or your proposed $. The ‘out’ keyword works the same way, but denotes a write-only parameter. You can read a bit more about that here. C# of course has no pointers except perhaps in “unsafe” code blocks. I think objects still have reference semantics by default though (could be wrong; I don’t know C# very well).

IIRC, the D programming language also has something along these lines, although it is a systems programming language and so it unapologetically includes pointers.

Since you seem to be interested in programmer safety/productivity in C-like languages, you might want to read more about D; its stated goal is to improve on C/C++ in exactly those ways, so it seems to be right up your alley. :D
@0x4A45535345

However, if the variable being passed is already the pointer of this address, then the & will not appear in the function call and it will hence no longer make it as easy to realize that a variable could be being modified there.

This sort of thing is hard to avoid if you want to make references/pointers first-class values (meaning you can declare variables that hold them, anywhere you like). It’s one argument for using Hungarian or some other kind of naming convention that distinguishes pointers from non-pointers, though obviously such things aren’t foolproof.

860fe478a2545d6c07b88c759292499e
0
SmokingRope 101 Feb 03, 2010 at 00:49

how about strictly enforcing variable naming

void add_five( mutableIntegerReferenceParameterValue )
{
  mutableIntegerReferenceParameterValue += 5;
}

that would be ultimate readability, assuming very widescreen monitors

Ff1a7adf06dc94b6271e4e632ce4aac0
0
0x4A45535345 101 Feb 05, 2010 at 22:42

It took me a few days to reply, so sorry for the delay.

Anyway, thanks for the info Reedbeta, I’ll look into it more. I’ve heard of C# and D before but I wasn’t really aware of what their features were (except perhaps roughly what style they follow).

how about strictly enforcing variable naming

void add_five( mutableIntegerReferenceParameterValue )
{
  mutableIntegerReferenceParameterValue += 5;
}

that would be ultimate readability, assuming very widescreen monitors

I’m inclined to disagree, SmokingRope.

I’m not sure if you were being sarcastic or not, but I would argue that that would actually make it less readable overall. Having such long names can easily decrease readability and it could make larger expressions so long that it would become quite a handicap. It’s just too verbose, to the point of being impractical. Alternatively, I suppose you could argue that showing all those traits as succinct symbols might have some merits, but it would crowd the already very limited selection of symbols and it would be hard to type.

The idea is to make programming more practical, faster, and more foolproof. As much as we want to say we don’t make mistakes, we often do. Being verbose doesn’t necessarily decrease mistakes, and can in fact easily increase both the number of mistakes and the amount of work required to make the code.

Interesting idea for defining the variable’s traits in the name though, I admit.

On a different subject, I’ve been thinking some more, and I think I’ve come up with a way to get the benefits of both references and pointers at the same time. I’ve changed my mind, and I think that allowing the pointers more directly would be better so that the language remains general. It should work out pretty well. It’s simple and should be fairly trivial to implement.

I’ve started writing up language traits and documentation for some things I think should perhaps be part of an improved language (compared to C/C++, Java, etc). It’s a work in progress, and I’m not letting it distract me from making programs in the current languages of course, but it’s an interesting topic to think about on the side.

Anyway, that’s it for now I guess.

36b416ed76cbaff49c8f6b7511458883
0
poita 101 Feb 06, 2010 at 21:35

The initial reason references were added to C++ was to allow overloading of operators that deal with lvalues, e.g. operator=.

How do propose that those operators should be implemented? Should operator= indicate to the user that it might cause a change?

$a = b; // ???

In my opinion, there’s no need to indicate whether a value might change. It seems fairly obvious to me that swap(a, b) is going to change them, and I think in all cases it should be obvious what’s going to be changed and what’s not (after all, if you don’t know what a function does then why are you using it?).

I would however, like to see multiple return values to reduce the need for using references as output. Something like:

(float x, float y) polar2cart(float r, float arg)
{
  return (r*cos(arg), r*sin(arg));
}

int main()
{
  int x, y;
  (x, y) = polar2cart(1.0f, 0.0f);
}

Of course, in this example the (x, y) pair should be packaged as a single struct, but there are many cases where you would like to return multiple things and creating a class just for that would be tedious.

B2d356f97a3d0dec8ae2d8c1e1fa1c2d
0
Nerd_Skywalker 101 Feb 07, 2010 at 03:47

@poita

I would however, like to see multiple return values to reduce the need for using references as output. Something like:

(float x, float y) polar2cart(float r, float arg)
{
  return (r*cos(arg), r*sin(arg));
}

int main()
{
  int x, y;
  (x, y) = polar2cart(1.0f, 0.0f);
}

Now THERE’s something i want to see.

Fd80f81596aa1cf809ceb1c2077e190b
0
rouncer 103 Feb 07, 2010 at 13:55

I wonder why that wasnt implemented, seems fairly useable to me. I often find being limited to one return variable “slot” is a pain too.

860fe478a2545d6c07b88c759292499e
0
SmokingRope 101 Feb 07, 2010 at 15:55

@0x4A45535345

I’m not sure if you were being sarcastic or not, but I would argue that that would actually make it less readable overall.

Interesting idea for defining the variable’s traits in the name though, I admit.

Sorry, was not being sarcastic. I think too that it is an interesting idea. And it is *perhaps* an outside-the-box solution to a number of the problems you’d like to address.
@ReedBeta

It’s one argument for using Hungarian or some other kind of naming convention that distinguishes pointers from non-pointers, though obviously such things aren’t foolproof.

You could reduce the overall length of the keywords using more cryptic symbols, (much like hungarian notation) however then your code has become … cryptic. However, enforcing whatever naming conventions at compile-time does help minimize the problems inherent with such things.

On a similar topic qualifiiers such as ‘$’ and ‘&’ are crypyic and using them during method invocation and declaration could easily cause the same mixed interpretations of the code by novice programmers.

6837d514b487de395be51432d9cdd078
0
TheNut 179 Feb 07, 2010 at 16:23

@Nerd_Skywalker

Now THERE’s something i want to see.

LUA does that :) I guess in defense of the way things are, single return values avoid programmers from making mistakes. Enforcing object use is tedious, but it’s consistent.

Best thing a new programmer can do is write these tests and see first hand how things work. You can read and forget about things easily, but working on it with your own hands has a better chance of being retained.

0b47321a3802325743015527383ece70
0
DevFred 101 Apr 02, 2010 at 12:16

@Nerd_Skywalker

Now THERE’s something i want to see.

boost::tuple and boost::tie to the rescue :)

#include <boost/tuple/tuple.hpp>
#include <cmath>

boost::tuple<float, float> polar2cart(float r, float arg)
{
    return boost::make_tuple(r*cos(arg), r*sin(arg));
}

int main()
{
    float x, y;
    boost::tie(x, y) = polar2cart(1.0f, 0.0f);
}