andrewducker: (Default)
andrewducker ([personal profile] andrewducker) wrote2010-02-11 03:11 pm

Thinking about code

  If I have a method:
int DoSomething(string someStuff)
{
  //Do Stuff
  return 42;
}
then effectively I have an unnamed variable that gets set by the return statement, yes?

That being the case, wouldn't it be in some ways clearer to have an explicit, named, variable that gets set instead?

int DoSomething(string someStuff)
{
  //Do Stuff
  returnValue = 42;
}

where "returnValue" is a keyword that's used to return the value.

As it is I frequently end up with code that creates (or sets) a variable at various points through the code just so it can be returned at the end.  Making this an explicit part of the language just makes sense to me.

I assume there are languages out there that do this.

[identity profile] bracknellexile.livejournal.com 2010-02-11 03:18 pm (UTC)(link)
VB lets you do both in that you can have:

Function DoSomething(someStuff as String) as Integer
  'Do Stuff
  Return 42
End Function

or:

Function DoSomething(someStuff as String) as Integer
  'Do Stuff
  DoSomething = 42
End Function

where it lets you use the function name as a variable of the same type as your explicit named variable that will be returned.
Edited 2010-02-11 15:19 (UTC)

[identity profile] bracknellexile.livejournal.com 2010-02-11 03:23 pm (UTC)(link)
Yeah, VS2003 onwards.

[identity profile] cartesiandaemon.livejournal.com 2010-02-11 03:22 pm (UTC)(link)
I was going to mention VB. I think the choice of using the function name, while I can see why it makes sense, is unfortunately confusing during the body of the function.

[identity profile] meihua.livejournal.com 2010-02-11 03:23 pm (UTC)(link)
Yep, you can ever do this:

Function DoStuff As Integer
   'do stuff
   DoStuff = 4
   DoStuff = DoStuff + 1
End Function

Msgbox CStr(DoStuff())   'Returns "5"

[identity profile] cheekbones3.livejournal.com 2010-02-11 06:02 pm (UTC)(link)
Beat me to it!

[identity profile] momentsmusicaux.livejournal.com 2010-02-11 03:42 pm (UTC)(link)
I can't decide whether that's silly or perlish.

Ah. Yes. It's silly because if you set $return halfway down your function, you can't tell at the end that you are returning something. Potentially hard to work out what is going on.

I'm not sure what you mean by an unnamed variable being set though... maybe that's a C# thing?

[identity profile] momentsmusicaux.livejournal.com 2010-02-11 03:54 pm (UTC)(link)
Why does that matter though?

[identity profile] skington.livejournal.com 2010-02-11 04:23 pm (UTC)(link)
You could be writing in a language which either doesn't need to declare variables for pretty much everything, or can return more than one variable.

e.g. a fibonacci function in Perl.

{
my @fib = (0, 1, 1);
sub fib {
my ($num) = @_;
return if $num < 0 || $num != int($num);
return $fib[$num] if $fib[$num];
return fib[$num] = $fib[$num - 2] + $fib[$num -1];
}
}

No need for a return variable there.

[identity profile] momentsmusicaux.livejournal.com 2010-02-11 05:12 pm (UTC)(link)
Oh that's because you're in a silly language that makes you declare variables!

I mean, "Messages messages = new Messages();" -- what the fuck is that meant to say? Gibberish!

I never got on with programming back when I was trying to do VB macros. Then I found perl....

[identity profile] andrewhickey.livejournal.com 2010-02-11 06:01 pm (UTC)(link)
Yep, because

#:: ::-| ::-| .-. :||-:: 0-| .-| ::||-| .:|-. :||
open(Q,$0);while(){if(/^#(.*)$/){for(split('-',$1)){$q=0;for(split){s/\|
/:.:/xg;s/:/../g;$Q=$_?length:$_;$q+=$q?$Q:$Q*20;}print chr($q);}}}print"\n";
#.: ::||-| .||-| :|||-| ::||-| ||-:: :|||-| .:|

Makes *SO* much more sense... ;)


(I have to use Perl at work, and just did a Java module on my Master's course. A pox on both their houses - give me a *proper* language any day...)

[identity profile] momentsmusicaux.livejournal.com 2010-02-11 06:11 pm (UTC)(link)
Firstly -- the lines starting with # are just comments, surely... oh hang on, you're opening your own script. That's just silly. Second, of course it's illegible -- you haven't spaced it out at all! Third, use the idiom. Don't nest an if directly in a loop; use unless - next to first skip over things you don't care about. Use m// in list context to capture to named variables rather than rely on $1 if you want to use it beyond the immediate syntactic vicinity of the m// (or the s///). You can write crap in any language ;)

[identity profile] andrewhickey.livejournal.com 2010-02-11 07:16 pm (UTC)(link)
That's not *my* code - that was the winner of the 2000 Obfuscated Perl Contest ;)

But, sadly, it looks like some of the scripts I have to debug at work...

[identity profile] momentsmusicaux.livejournal.com 2010-02-11 07:36 pm (UTC)(link)
That'll be why it's crazy then!!! ;)

I agree, some HORRIBLE things can be written in Perl. Probably more so than in other languages. I still like it though :)

[identity profile] momentsmusicaux.livejournal.com 2010-02-11 10:27 pm (UTC)(link)
Yes, but it's called the same as its type! Arg! And you are having to repeat the type name -- that's a total violation of DRY, surely.

For that matter, why do you need a type as specific as a message list? Why is that not just an array?

[identity profile] momentsmusicaux.livejournal.com 2010-02-12 11:02 am (UTC)(link)
Ah, so it's an object rather than just a data type? I can see that would be useful.

[identity profile] skington.livejournal.com 2010-02-11 03:43 pm (UTC)(link)
You don't have an unnamed variable. You have, in this case, a constant that is passed back to the caller, without any memory allocation.

Now, having syntactic sugar where saying functionname = might be useful in some cases, although the fact that it looks and behaves like a normal variable, but has special behaviour based on the fact that it's called the same thing as the function, is something that would make me uncomfortable: you have to remember to update that variable name if you change the function name, or cut and paste some code from one function to another.

Also, if your function doesn't have a name (in the case of anonymous functions and/or closures), you can't do that. So you have to fall back to the far more obvious and readable return statement.

I think if you've got a number of different return values, that you'll decide according to various criteria, scattered through the code of the function, and you then finally return the value after having done all your processing, then it makes sense to declare a return variable before all of your ifs and switches etc. and then return it.

But I don't see why you'd want the overhead of doing that in all cases.

(I'm coming at this from a Perl, and occasionally Javascript, background, FWIW.)

[identity profile] skington.livejournal.com 2010-02-11 03:50 pm (UTC)(link)
Why do you care, as the called function? It ends up on the stack or heap, your job is done.

As the calling function, you'll assign the result to a variable of your choice (or not assign it at all, if you can do that sort of thing and you only care whether the function returned e.g. a true value, or you'll pass the value to a switch statement).

[identity profile] skington.livejournal.com 2010-02-11 03:57 pm (UTC)(link)
So you basically want exactly the same as the return keyword, except you can call it at any time without actually returning. So what if you want to return part-way through your function? Do you now have to use two keywords?

[identity profile] jsburbidge.livejournal.com 2010-02-11 04:45 pm (UTC)(link)
In a decent optimising compiler, a constant value return isn't "put" anywhere except outside the function, where it is used as a precompiled/hardcoded value (which may in itself be used in a context allowing further precompilation reducing runtime overhead). In particular, it may never be put on the stack to be returned, although it may be used to set/initialize a value in the calling context.

As an extreme example, imagine that the function is called in the context

if (DoSomething(x)) {
... stuff
} else {
... other stuff
}

The compiler should never actually store "42" but will instead elide the else branch of the test.

[identity profile] call-waiting.livejournal.com 2010-02-11 06:54 pm (UTC)(link)
Only if the compiler does interprocedural analysis, which not many do. Alternatively, it could be inlined, but most will only do so if it's in the same compilation unit, and you ramp up the optimiser setting sufficiently.

[identity profile] jsburbidge.livejournal.com 2010-02-11 07:24 pm (UTC)(link)
I was thinking of the "same compilation unit" case, in fact. I was also deliberately ignoring the fact that the obvious reason for returning a constant is the implementation of an interface (C++: of a virtual function) for a family of functions which may return different values, in which case obviously there will be no optimisation when the interface / base class is used.

One variant of this optimisation which is more common, though, is returning an object by value and assigning to a class constructed at that point:

Foo x = somefunction();

For some compilers the object will be constructed once, at the higher level, and there will be no temporary variable of that type in the returned data on the stack (which will be data required to initialize the object -- the difference obviously being that operations in the constructor are called only once).

[identity profile] nmg.livejournal.com 2010-02-11 04:04 pm (UTC)(link)
Lisp and Scheme do something orthogonal, but related; the value returned by a lambda expression is the value of the last expression evaluated in it.

For example (in Scheme), if we have a function:

(define fib
  (let ((ult 0)(pen 1))
    (lambda ()
      (let ((tmp pen))
        (set! pen (+ ult pen))
        (set! ult tmp)
       ult))))


Each time fib is called, the value returned is that of ult (set! returns an unspecified value).

If we wrote this in Common Lisp instead (where setq returns the value used to set the variable) we could write this instead:

(let ((ult 0) (pen 1))
  (defun fib ()
    (let ((tmp pen))
      (setq pen (+ ult pen))
      (setq ult tmp))))


Here, the value returned by fib is the value of the final setq, which is the value of tmp.
Edited 2010-02-11 16:05 (UTC)
simont: A picture of me in 2016 (Default)

[personal profile] simont 2010-02-11 04:13 pm (UTC)(link)
Nobody's mentioned Pascal yet, so: assigning to the name of the function is the way to set the return value in Pascal.

[identity profile] lpetrazickis.livejournal.com 2010-02-11 04:22 pm (UTC)(link)
I prefer having multiple return statements. Languages that have this sort of a variable also tend to not allow for early function termination, forcing you to wrap your code in layers of conditionals.

Languages with a return variable:

function monkeys() {
if (foobar) {
return = 42;
} else {
// ...
if (dagnabit) {
return = 13;
} else {
// ...
if (consarnit()) {
return = e^(-i*pi)
} else {
// ...
}
}
}
}
// fall out of function
}


Language with a return statement:

function monkeys() {
if (foobar) return 42;
// ...
if (dagnabit) return 13;
// ...
if (consarnit()) return = e^(-i*pi);
// ...
}


I find the latter preferable.
ext_8559: Cartoon me  (Default)

[identity profile] the-magician.livejournal.com 2010-02-11 05:16 pm (UTC)(link)
VB has the "exit" command for exiting loops

For nRow = 1 To nLastRow
If ws.Cells(nRow, nColumn).Address = "$A$7" Then
Debug.Print "Found cell. Exiting for loop."
Exit For
Else
Debug.Print ws.Cells(nRow, nColumn).Address
End If
Next

In some ways I like having the function name as the return variable, since the calling line will have something like

b = f(a) + 4;

and the code will have

int f(int a){
f = 3*a;
}

so mentally f is a function that returns an int *and* is an int. Of course in some languages you'll then have problems if you ask for the address of f (is it a pointer to the nominal variable, or a function pointer?)

And it gets even more fun with recursive calls!

I quite often have code like (vastly simplified)

string center(string astring) {
string mystring;
mystring = "";
if len(astring) > 0 then mystring = "<center>" + astring + "</center>";
return mystring;
}

[identity profile] draconid.livejournal.com 2010-02-11 08:16 pm (UTC)(link)
I'm sure I've used a language whereby, if you don't explicitly return something, it automatically returns whatever the value of the last line/expression is. I have no idea which one that is though!

[identity profile] call-waiting.livejournal.com 2010-02-11 09:13 pm (UTC)(link)
perl'll do that.

[identity profile] xraycb.livejournal.com 2010-02-12 12:12 am (UTC)(link)

In matlab you declare the name of your result variable in the function definition:

function outvar=func_name(in1, in2)
outvar = in1 + in2;

multiple results are returned like this:
function [out1 out2]=func2(in1, in2)
out1 = in+1;
out2 = in+2;


In Eiffel, you set the result of a function by assigning to a compiler-created variable called Result.

[identity profile] undeadbydawn.livejournal.com 2010-02-12 04:11 pm (UTC)(link)
I read this thread because code [like Logarithms] is an abject mystery to me. I keep on believing that one day it'll magically make sense.

nope. not today. Brain is goo, again.