[PUP-9695] Add ability to return multiple named values from a function. Created: 2019/05/08  Updated: 2019/05/13

Status: Open
Project: Puppet
Component/s: Language
Affects Version/s: None
Fix Version/s: None

Type: New Feature Priority: Normal
Reporter: Thomas Hallgren Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Sub-team: Language
Team: Froyo
QA Risk Assessment: Needs Assessment


This is inspired from Golang where it is possible to return several values from a function and also to declare variables as part of the declaration of the return value.

Consider the following function:

function multiReturn() >> Struct[a=>String,b=>Integer] {
  $a = 'hello'
  $b = 3
  return { a => $a, b => $b }

this could instead be written as:

function multiReturn() >> (String $a, Integer $b) {
  $a = 'hello'
  $b = 3

The result would in both cases be:

{ a => 'hello', b => 3 }

This new syntax also aligns well with the already existing ability to assing multiple returns to named variables of an Array, e.g.

[ $a, $b ] = multiReturn()

Comment by Henrik Lindberg [ 2019/05/08 ]

I would restrict this so that the "assignment sets one returned value" only is available when the return is specified using the new syntax.
Further, when return is specified this way, variables must be assigned, and that if they are not, the return specification must allow an undef value. An error would be raised otherwise.

A corner case is if logic assigns to "return variables" but then explicitly return a hash (calling return()).
Can you call return() at all - what does that mean?

Comment by Henrik Lindberg [ 2019/05/08 ]

Further details:

  • it should be allowed to specify that an input parameter is also returned - and when done this way, the type of the returned does not have to be specified since it is defined by the parameter. (naturally, It would be allowed to narrow it).

    foo(String $a) >> ($a) { }

  • An assignment must be made in the function's scope - cannot do this in a nested inner scope, and the assignment requirement cannot be fulfilled by a variable set in an outer scope.
Comment by Henrik Lindberg [ 2019/05/08 ]

Question, should it allow default values to be used if the variable is not assigned?

Comment by Thomas Hallgren [ 2019/05/08 ]

Not sure if it makes sense to be able to narrow the returned type when passing a parameter, given that the passed variable is immutable.

I think allowing defaults would be a good thing. Without it, defaults must be explicitly stated within the function itself and perhaps in multiple places. That can be a tedious task since the parameter is immutable.

It should be allowed to call return(), but only without arguments. We could allow returning a hash but I don't really see the point in that and it introduces new questions. What if the hash contains more variables? Is that allowed? If it contains fewer variables? Will the declared variable be used in that case? Etc.

Comment by Henrik Lindberg [ 2019/05/08 ]

Basically that means that you must return the specified variables (you can set them to undef (or not set them and get undef if that is allowed). You can for example not specify that the function can return an undef instead of a Hash. (I am fine with that). Then, a return() is the same as return(undef), and the return must be caught to transform the returned value to what is specified. A return of NotUndef would be an error.

Comment by Henrik Lindberg [ 2019/05/08 ]

On narrowing - not important, but the function may guarantee that a returned value is narrower than the input - it could do its own type assertions for example.

Comment by Thomas Hallgren [ 2019/05/08 ]

I don't think that a function that declares multiple return values like this should ever be able to return an undef. It will always return a hash containing the values of the variables. This is analog with a function that declares that the returned type is a Struct. It's not allowed to return undef. Just calling return() without arguments should mean "return here using whatever value that the variables have at this point". The sole purpose of doing that is to break out of the function at some other place than at the end.

Comment by Henrik Lindberg [ 2019/05/08 ]

Ok - those constraints are good. I was pointing out that return() is a call, and a return without value is a "return(undef)". A function that has the "output variables" declared would need to catch the return and change it to a return of the struct (and error if user called return with something other than undef. At the moment there is no difference between return(undef) and just undef().

Generated at Tue Jul 14 04:21:07 PDT 2020 using Jira 8.5.2#805002-sha1:a66f9354b9e12ac788984e5d84669c903a370049.