Multi returns in Lua

I didn’t want to hijack this thread on the top 10 programming languages

There was a question about having multiple returns.

This is from the Lua manual

An unconventional, but quite convenient feature of Lua is that functions may return multiple results. Several predefined functions in Lua return multiple values. An example is the string.find function, which locates a pattern in a string. It returns two indices: the index of the character where the pattern match starts and the one where it ends (or nil if it cannot find the pattern). A multiple assignment allows the program to get both results:


    s, e = string.find("hello Lua users", "Lua")
    
    print(s, e)   -->  7      9


Functions written in Lua also can return multiple results, by listing them all after the return keyword. For instance, a function to find the maximum element in an array can return both the maximum value and its location:


    function maximum (a)
      local mi = 1          -- maximum index
      local m = a[mi]       -- maximum value
      for i,val in ipairs(a) do
        if val > m then
          mi = i
          m = val
        end
      end
      return m, mi
    end
    
    print(maximum({8,10,23,12,5}))     --> 23   3


Lots of other things that this can be used for.

Exactly the same as in Python.

Too often I find myself overusing this, returning 4+ primitives, when adding a data transfer object would make things much less confusing.

I started with Lua, so when I got to Java and couldn’t return multiple values it was a “what” moment.

It is incredibly useful to me, but the potential for problems is there. When more than 3 values are being returned, it’s very easy to forget the order in which you are returning things. As a general rule, I avoid multiple returns for anything over 3, preferring to return a table* with named indexes.

*Tables are really awesome and incredibly unfair. Transitioning from those to arrays or hashmaps makes me very, very appreciative of their magic.

You can do this as well in Golang! It’s super useful. In fact, you can actually just use return (with no arguments) to return all method-scope non-argument values.

Yes they are, the key to Lua is learning to use tables as on of the first things you do.

Add Matlab to the list of languages with multiple returns and a similar format.

Technically, you aren’t returning multiple values, you’re returning one tuple containing them. But yes, this behavior is very useful and is part of why it’s so easy to write such short, concise Python code.

Multiple returns are possible just about anywhere, but in many non-interpreted languages (C, C++, etc), and some interpreted ones, they’re generally listed as bad practice for a number of reasons.

Ruby has one of my favourite forms of multiple returns, where an array is returned and “exploded” into a variable list using a syntax trick. You can either return an array, but if you want to use the below syntax trick, you must use the return keyword (normally in Ruby, the last line executed in a function is assumed to be the return value unless stated)


def myfunc
    return 1, 2
end

a, b = myfunc
puts "#{a}, #{b}"       # => 1, 2
c = myfunc
puts "#{c}"             # => [1, 2]

a, b =  3, 4, 5 ]
puts "#{a}, #{b}"       # => 3, 4 (all other indexes are discarded)

It also has a “splat” operator (*) for taking an array and “splatting” it into a function argument list


def myfunc a, b, c
     puts "#{a}, #{b}, #{c}"
end

myfunc *[1, 2, 3]           # => 1, 2, 3

For those using C/C++, it’s pretty common to do multiple returns with pointer assignments. You can do the same thing with references, both examples are below.


int myfunc(int in, int *out) {
    *out = in+1;
    return in+2;
}

int myfunc2(int in, int &out) {
    out = in+1;
    return in+2;
}

int main(void) {
    int a, b = myfunc(1, &a);    // a = 2, b = 3
    int c, d = myfunc2(1, c);    // c = 2, d = 3
    return 0;
}

If your variables are stack allocated, you can even pass in the first one as a pointer and write to it as if its an array, but that’s bad practice and dangerous for many many many many many many many reasons.

Multiple returns or multiple values returned?
Your examples are multiple values returned.

If you think returning multiple things is bad how would you return an object with multiple properties without engaging in bad practice?

Consider how some languages communicate errors.

There are certainly a great number of processes which inherently generate multiple values and/or arrays. Jaci listed a good one, and there are many tokenizers, signal processes, image processes, financial calculations, and such which produce multiple outputs.

I find the multiple-return function, as bad as that sounds mathematically, to be a cleaner programming solution than pass-by-reference. I generally eschew side effects of my r-values (that is, changing the value/state of stuff which is only on the right side of an assignment statement), to the extent that I rarely use ++/-- in an assignment, but mostly use ++/-- as a standalone statement.

There are obviously many schools of thought on this, and I get why different languages have different takes on it, but I like code to loudly proclaim which variables/objects may change their value/state as a result of processing each line/statement/command by putting it/them on the left side of the assignment operator. This is especially true when more than a few days have elapsed since the code was written. Hidden side effects are the devil’s workshop.

Edit: Personally, I’d like if object oriented languages (particularly those more strongly typed, like java) gave you a chance to specify which of its methods modify the state/value of an object, and provide that objects passed to a function/method can only use the non-modifying methods unless specifically declared otherwise (e.g. modifyable) in the function declaration. I can dream, can’t I?

Multiple returns IS multiple values returned with some syntactical sugar. The ruby example is practically identical to the Lua one, except with the convenience of being able to access it as an array (great for when the size only becomes known at runtime).

As for the C example, if I’m sending multiple values up a scope, whether they’re packed inside a data structure, an array, or just on their own, it’s still returning multiple values. The C example is notable because languages like Java don’t allow for direct manipulation in that way.

When I say “bad practice”, my meaning is in returning multiple values with the same return statement, non-packed (return a, b. Not return [a, b]). Note my original statement: “in non-interpreted languages, multiple returns are generally considered bad practice”. Obviously there are exceptions, like Go. The reason they are considered bad practice, and not included in many languages, is typically because they’re difficult to chain (func2(func1())), because some values may be discarded, and because some values might go unused. All of these make it tough for the compiler (even moreso when types get involved), and don’t represent what is actually happening to the data (which in almost every case is being packed into an array on one side, and unpacked on the other side, which begs the question, why not use an array in the first place?).

Some languages embrace multiple returns, but for many they just don’t fit into the paradigm of the language. In Lua, Go and Ruby, they’re a large part of how the language is used, but in many other languages it’s typically preferred to just use a data structure or an array.

I ask because multiple points of return from a function can make for confusing exit conditions. IE calling return in conditionals for example.

As for the C example, if I’m sending multiple values up a scope, whether they’re packed inside a data structure, an array, or just on their own, it’s still returning multiple values. The C example is notable because languages like Java don’t allow for direct manipulation in that way.

When I say “bad practice”, my meaning is in returning multiple values with the same return statement, non-packed (return a, b. Not return [a, b]). Note my original statement: “in non-interpreted languages, multiple returns are generally considered bad practice”. Obviously there are exceptions, like Go. The reason they are considered bad practice, and not included in many languages, is typically because they’re difficult to chain (func2(func1())), because some values may be discarded, and because some values might go unused. All of these make it tough for the compiler (even moreso when types get involved), and don’t represent what is actually happening to the data (which in almost every case is being packed into an array on one side, and unpacked on the other side, which begs the question, why not use an array in the first place?).

In languages that do not support this capability I usually create a consistent way to pack, unpack and iterate, and where possible leverage that code to insure things like ordering and optimal sorting where relevant. Granted other people may fail to do that, and then that is bad practice. Effectively expanding the capability by user functions. In languages that do support these structures it is usually faster to use their syntax…especially in interpreted languages because they can run compiled/optimized functions to pack/unpack/iterate. If I know someone might fail to grasp the imposed structure I try to create that structure to cause obvious QA failure as soon as possible…either by offseting the structure so the first element is consistently something like a null or insuring common functions are wrapped to check for the data structure and react.

Some languages embrace multiple returns, but for many they just don’t fit into the paradigm of the language. In Lua, Go and Ruby, they’re a large part of how the language is used, but in many other languages it’s typically preferred to just use a data structure or an array.

I agree with the idea that where possible the return from a function should be grouped and organized into a structure with a clear nature and boundaries. Just returning random values, or abusing local or broad globals causes trouble.

Thanks for starting this thread!
Lots of good discussion here. Im glad im not the only programmer who lurks cd in the summer :wink:

Im going to be experimenting with using lua and c++ in conjunction soon (previously java & webapp dev) so hopefully I find these multi returns beneficial and not confusing

It’s pretty easy to attach Lua to C/C++, there are lots of good examples at the Lua site to follow. I recommend getting the latest version of Programming in Lua and follow along with that.

Good luck!