Syntactic sugar for returning array of values

I had an idea just now that I thought was pretty cool, so I'm curious to see if anyone agrees. The idea is to allow a function which returns an array of values to define those values one by one, using expressions which evaluate to unused values. Here's an example:

func rectTransforms () -> [RectTransform] {

    RectTransform { rect in
        rect.width = rect.width * 2
    }

    RectTransform { rect in
        let previousWidth = rect.width
        rect.width = rect.height
        rect.height = previousWidth
    }

    RectTransform { rect in
        rect.x += rect.width
    }
}

In case it's unclear for anyone - in this scenario there is a type called RectTransform which has an initializer: init (transformFunction: @escaping (inout Rect)->()) such that you can create new instances in the manner seen above. The unsugared version of this might be something like:

func rectTransforms () -> [RectTransform] {
    var returnValues: [RectTransform] = []

    let value1 = RectTransform { rect in
        rect.width = rect.width * 2
    }
    returnValues.append(value1)

    let value2 = RectTransform { rect in
        let previousWidth = rect.width
        rect.width = rect.height
        rect.height = previousWidth
    }
    returnValues.append(value2)

    let value3 = RectTransform { rect in
        rect.x += rect.width
    }
    returnValues.append(value3)

    return returnValues
}

Clearly, the former is vastly preferable from a syntax perspective. If the whole use case were totally contrived that wouldn't matter, but I hope you can see from a combination of the example and your imagination that there could be many ways to apply this create beautifully elegant descriptions of rather complex values. An extension of this example might be to describe complex animations in a similar manner. The difference between the sugared and unsugared versions is especially dramatic because the return values each use trailing closure syntax - this is on purpose, because the use case I have for this does the same thing which is why I want this.

Swift has already set the precedent of floating values being treated as return values in single line closures. This seems to me like a debatable but fairly natural extension of that. I personally have a use case for this in a library I'm developing.

@discardableResult functions complicate things slightly, but not really. If you want to use one for its side effects within a function like this without returning the value, just do this let _ = mySideEffectFunction(). If you use normal return myArray syntax then you can of course use mySideEffectFunction() without the trivial assignment to _ without any unintended return values.

I get that there are all kinds of questions to answer, like "what if the user puts at the bottom of the function if someBool { return [] }?" and many others. Will love to hear what ideas you guys have about this.

Just add some brackets and commas, you can already write it like this:

func rectTransforms () -> [RectTransform] {
  return [RectTransform { rect in
    rect.width = rect.width * 2
  },
  
  RectTransform { rect in
    let previousWidth = rect.width
    rect.width = rect.height
    rect.height = previousWidth
  },
  
  RectTransform { rect in
    rect.x += rect.width
  }]
}

…and with SE–255, soon you won’t even need the “return” anymore!

10 Likes

I think this could fall out pretty easily from function builders.

3 Likes

Well, I still haven't quite figured out what's going on, but SwiftUI seems to be employing exactly the syntax I was requesting (for exactly the reason I was requesting it)

2 Likes