Accessing only a subset of function outputs

suppose that there is a function with several return values

function foo() 
  ...
  ret1, ret2, ret3
end

and suppose that a caller is interested in a particular subset of the return values and not all of them.

How could this be implemented in an expressive manner?

My thoughts:

  1. If only the first output is desired, then it is straightforward

    ret1 = foo() # ret2 and ret3 are not going to be accessible in the caller scope

  2. if only one output is desired,

    I could imagine returning the outputs as a named tuple, e.g.

function foo() 
  ...
  (x = ret1, y  = ret2, z = ret3)
end 

y = getindex(foo(),:y)   # the caller does not need to store references to x and z in dummy variables 
                         # and implies that only y is going to be used 

I am wondering if there is still a better way to express this and how to handle case 3, i.e. if only accessing more than one output is desired.

It is also possible to access a particular output in this way:

y = foo()[2] # with the assumption that caller knows the order of the outputs

I like this:

julia> foo() = return 1,2,3
foo (generic function with 1 method)

julia> foo()
(1, 2, 3)

julia> (_,x2,_)=foo();

julia> x2
2
2 Likes

Does employment of named tuples for outputs come with any syntactical advantages in this context?

Depends on your problem.
With named tuples you could do:

julia> foo() = return (x1=1,x2=2,x3=3)
foo (generic function with 1 method)

julia> r=foo();

julia> r.x1
1

I don’t think it gives anything worthy.

This is clear. However, the idea is not to make non-used outputs accessible in the caller scope or use additional dummy references.

Named tuples would be beneficial if

  • there are a lot of outputs
  • no need to memorise the order of the outputs
  • avoid potential errors (i.e. when confusing the order of the outputs of the same type)
  • allow modification of the functions with additional outputs (without necessarily modifying the programs that calls such functions)
  • …

I personally like NamedTuples for multiple returned values because if I’m refactoring later and need to add additional returned values, I don’t have to worry about how the order might affect every single call site. And should I later decide that the set of returned values is coherent enough to be a struct, I’ve already started accessing fields with properties so it’s easier to find and replace them.

But really, does something “come with any syntactical advantages” is really up to your experience of what is more readable and useful!

1 Like

Since Julia 1.7 you can use property destruction (JuliaLang/julia#39285) as follows:

julia> foo() = (x1 =1, x2 = 2, x3 = 3);

julia> (; x2) = foo();

julia> x2
2

If your functions return too many things such that this is a problem I would rething the API and maybe break things down into smaller pieces.

This is already possible (unless going from 1 to more return values), for example:

julia> foo() = (1, 2, 3);

julia> _, b = foo();

julia> b
2
4 Likes