How to say "the value in parent scope" in a function

How do I modify the shadowed u inside the if-statement below?

func f () {
    print (#function, "...")
    
    var u: Int? = 29
            
    if var u {
        u *= u
        print ("\tinner u", u)
        // modify the outer u
        // can't, because it is shadowed by the unwrapped u
    }
    print ("\touter u", u!)
}

Workaround:

func g () {
    print (#function, "...")

    var u: Int? = 29
    
    let setu: (Int?) -> Void = {u = $0}
    
    if var u {
        u *= u
        print ("\tinner u", u)
        setu (u)
    }
    print ("\touter u", u!)
}
f ()
g ()
f() ...
	inner u 841
	outer u 29
g() ...
	inner u 841
	outer u 841
1 Like

Does this do what you want (probably the bellow can be further shortened / optimized)?

func f () {
    print (#function, "...")
    
    var u: Int? = 29
            
    if var u2 = u {
        u2 *= u2
        
        u = u2
        print ("\tinner u", u ?? 0)
    }
    print ("\touter u", u!)
}
1 Like

Thank you. Yes, I know it does, but it entails renaming the variable.

How about this?

var u: Int? = 27
u = u.map { $0 * $0 }
2 Likes

Sorry, my original post was poorly worded.

I have edited my post.

I apologise if my question sounds rhetorical, but currently there is no direct way to say the variable in the outer scope.

1 Like

How useful would a feature like this really be? Being explicit under a different name established good intention. This feels like when one sometimes inadvertently shadows a property that can get mixed up with implicit self.

2 Likes

Wouldn't something like this make the language more expressive?

func f () {    
    var u: Int? = g ()
            
    if let u {
       ../u = h (u)
    }
}

No, it would make the language less expressive.

If you want to access the outer binding from the inner scope, then do not shadow it: if you shadow the outer binding, then it expresses your intention that it cannot be accessed from the inner scope—this is a feature.

14 Likes

I've always wanted this feature. I generally just name things uglier than I want. But that's probably a bad idea these days; practicing civil disobedience in one area of your life, working to make life look like you want it to, will help you make it a habit for where it counts more. The best way I know of to accomplish that is a struct instance.

func f() {
  struct F {
    var u: Int? = 29
  }

  var f = F()

  if let u = f.u {
    f.u = u * u
  }
}

It seems to me that this would be a nice compromise?

func f() {
  f: do {
    var u: Int? = 29

    if let u = f.u {
      f.u = u * u
    }
  }
}
1 Like

But, how? If there is a logical explanation, let's hear it.

Being able to do this:

func f () {    
    var u: Int? = g ()
            
    if let u {
       ../u = h (u)
    }
}

is not diminishing the expressive power of the language; it is only enhancing it.

1 Like

As I said, with your example, a user could no longer express that they do not intend access to the first binding to be possible within the inner scope. That is strictly less expressive than the current state of the language, where you can already accomplish what you want by giving the second binding a different name.

Anyway, I suspect this conversation is entirely played out. The question about how to do what you asked using Swift has been asked and answered.

Nobody owes anybody else a response or their attention. This is the way to be kind, when you're done with it:

There are many ways to evaluate language features, but two criteria are always at the top of my mind:

  • is this feature an abstraction (something that can decrease the complexity of your entire program by a non-constant factor), or is it syntax sugar (a local replacement of a token sequence with a slightly shorter one)?
  • do any other well-designed languages have anything similar?

If you’re adding well-worn abstractions and sugar, then you’ve got a body of prior experience to draw upon. Thinking of adding a novel abstraction? It may or may not work out, but someone has to move the state of the art forward; good luck to you! Novel syntax sugar on the other hand, is one of those things that almost never turns out to be a good idea in hindsight.

6 Likes

One feature that’s been discussed is support for inout bindings. The idea is that you’d be able to perform a scoped mutating access of some location. Writes to the binding would update the location. Eg,

var u: Int? = 27
if var u = &u { // or whatever
  u *= u
}
5 Likes

This rationale is unconvincing to me. Nearly every time I see a binding shadowing another binding in Swift, the intention is to communicate "this is a non-optional version of the shadowed binding", not "I want to disallow access to the shadowed binding".


That being said, I don't think this feature would be useful enough to be worth adding, especially if we get inout bindings.

2 Likes

Another approach which I find myself following often is to do something like this:

if u != nil {
    u! *= u!
}

I don't love that I have to use ! inside the block, but it should be safe given the != nil check.

1 Like

That makes me feel wishing we had this:

Or even this:

var u: Int? = 27
if var u {
   // no shadowing
   // u is an unwrapped alias of optional u
   u *= u
}