Nice catch, fixed, thanks.

This addresses situations when the provided initializer of the type doesn't allow us to specify all the properties we want. I've fixed Point type in my example to illustrate this, but you might find more real world examples in this thread.

That's exactly what it does. do with _expr_ takes value of _expr_ and evaluates the body as if it an extension function called on the value. In other words it's almost equivalent to:

extension Point {
  func makeString(separatedBy separator: String) -> String {
    x + separator + y
  }
}

let separator: String = ","
let pointStr = Point().makeString(separatedBy: separator)

The differences is that the extension function requires all used variables from the outer scope to be passed as arguments. do-block can just use them as they are directly visible.

In case when transformation of Point to something else can be a single line expression it doesn't really matter, but this isn't always the case. Sometimes we might need more complex code with multiple statements.

let vector = Vector3D(x: 1.0, y: 2.0, z: 3.0)

let vectorInfo = do with vector {
    let xSquare = self.x * self.x
    let ySquare = self.y * self.y
    let zSquare = self.z * self.z
    
    let sumOfSquares = xSquare + ySquare + zSquare
    let magnitude = sqrt(sumOfSquares)
    
    print("Vector: (\(self.x), \(self.y), \(self.z))")
    print("Magnitude: \(magnitude)")
    
    // Return any value you want from the `do with` block
    (sumOfSquares, magnitude)
}

Note that this way:

  • we don't pollute the outer scope with variables that we won't need.
  • unneeded variables will be released upon leaving the scope, so we shorten their lifetime.
  • we don't introduce an extension that will be used once.
  • we still provide a clear visual hint that this code does something coherent.

I'm not quite sure what you meant by this example. If you meant this

let pointStr: String
do {
  let point = Point()
  pointStr = ...
}
// use pointStr

Then the benefits would be:

  • we are getting type inference to our disposal
  • we can omit some uses of variable names

If you meant something more nested, like this:

do {
  let point = Point()
  let pointStr = ...
  // use pointStr
}

Than the benefits would be:

  • we keep the "happy path" low-indented.
  • in case we are dealing with some throwable code inside the block, it's nice to have catch block as close as possible.

Please also note, that the pitch doesn't declare it introduces something to the language that is impossible now. It's all about convenience and ergonomics.