[swift-evolution] The bind thread

Thorsten Seitz tseitz42 at icloud.com
Tue Feb 2 11:38:19 CST 2016


> Am 02.02.2016 um 07:44 schrieb ilya via swift-evolution <swift-evolution at swift.org>:
> 
> I respectfully disagree with the need for bind operator, see below:
> 
> On Mon, Feb 1, 2016 at 8:18 PM, Erica Sadun via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
> Joe says "If you all are serious about this, I think you should start a new thread about it." 
> I think it's worth a serious discussion just so it can be evaluated and either adopted or discarded
> and dropped forever. Here goes.
> 
> INTRO
> 
> The if let x = x {...} and guard let x = x else {...} constructs do something with let (and var) that's 
> fundamentally different from let (and var) elsewhere in the language.  The same keywords are used to conditionally unwrap
> and bind an item, not just shadow that item's current value.
> 
> let A = B does exactly the same in all instances: evaluate the expression B in current scope and bind the result to a local name A, and if let x = x meaning is no different. If a person doesn't know the precise meaning of if let, with the similarity to normal let one is likely to guess at least the "name binding" part. This is a big advantage of if let syntax.

The meaning of `if let x = y` differs from `let x = y` in the type inferred for x (non-optional vs. optional). 


> 
> Introducing a new bind keyword to indicate unwrapping and binding would disambiguate these uses.
> 
> DETAIL DESIGN:
> 
> Jacob Bandes-Storch offers two common use-cases. I prefer his "if bind foo" to my original "if bind foo = foo":
> 
>   if bind foo {
>       // foo is non-optional in here
>   }
> 
> 
> Let's consider the case where foo was originally a property. Unfortunately, the syntax above is then very likely to confuse people into thinking that self.foo is non-optional inside the inner scope, which is far from guaranteed:

Actually that semantics (I would prefer `exists` to `bind` to make this more explicit in the syntax) is the whole point of flow typing.


> if bind foo {
>     // we have created a local variable foo here
>     // foo is non-optional
>     // no guarantees about self.foo can be made, however
>     // as it's value may have changed
> }
> 
> The same applies if foo is a local variable captured by an escaped closure which may be executing concurrently with the inner block.

In Ceylon flow typing is only allowed for constants (i.e. let bindings), probably for that reason, so foo would have to be constant and the problem you mention would not exist.

In Ceylon if `foo` is a variable you have to rebind to a constant which looks like follows (Swiftified, and I’m using `exists` rather than `bind` because I don’t like the latter for the aforementioned reason):

// foo is a var
if exists x = foo {
    // x is a let binding to foo
}

So this is exactly the same as Swift’s current `if let x = foo` except that it makes the unwrapping (or mapping as Erica pointed out) clear and can be used without a (possibly shadowing) assignment for constant values (`if exists x { … }` vs. `if let x = x { … }`).

As I already posted in the original thread `if var` would have to be expressed more clumsily as `if exists x { var copy = x … }` like originally proposed in SE-0003 (that part was recently thankfully revoked). With `exists` there would be no additional shadowing (`if let x = x { let copy = x … } `) so it might be not so bad.


There is a potential advantage of the current `if let` syntax, though: it would allow to be generalized to work for monadic types, i.e. it might just be desugared into `map`. 
This could be extended to `for` loops where `for x in foo, y in bar { … }` would be desugared into `flatMap` for all `in` expressions but the last which would be desugared into `map`. In both cases a `where` clause would be desugared into `filter`. For a type to be applicable in `if let` statements it would have to conform to a protocol providing `map`, for support of `where` clauses it would have to conform to another protocol providing `filter` and for being used in `for` statements with nesting it would have to conform to yet another protocol providing `flatMap`. 

In that case I’d definitely prefer to stay with the current syntax, because `let` makes more sense in a generalization than `exists` which only applies to optionals.

I think that’s the direction I’d prefer most.

-Thorsten



> 
> So it seems like the bind statement is most helpful only when foo is a local name and either declared with let or not captured by any escaped closure. In that case the original foo can be used directly without shadowing. 
> 
> One sees how such a statement can be useful, but I'm not sure if the word bind is the best one for it. After all, both let and var perform name binding. Also, there were suggestions to make 
> 
> // foo is local and not escaping
> if foo != nil {
>     // foo is non-optional
> }
> 
> simply work without any additional syntax. Perhaps one can look into that.
> 
>   somethingAsync { [weak self] in
>       guard bind self else { return }
>       // ...
>   }
> 
> JBS's approach offers my original "bind" keyword to unwrap and shadow bind, but also provides a way to 
> strongly bind a weak reference to self, which (presumably) would allow self semantics in the remaining
> lifetime of that scope.
> 
> ALTERNATIVE PROPOSALS:
> 
> Tino Heth proposes a second use-case one with different semantics. This case, it seems to make an
> alias rather than using binding for shadowing:
> 
> bind x = a.property.with.a.long.path
> print x  // 42
> print(a.property.with.a.long.path == 42) => true
> 
> presumably this means:
> 
> x += 1
> print(a.property.with.a.long.path)  // 43
> 
> DISCUSSION
> 
> I'm throwing these both out there. I have nothing to really say about Tino's but I do think my and Jacob's 
> proposal has the advantages of:
> 
> * Simplifying an mildly complex and potentially misleading statement 
> * Creating a deliberate and controlled rather than accidental shadowing style
> 
> Have at it.
> 
> 
> The 'big picture argument'  is that shadowing is likely to introduce more errors, so we should be discouraging it, not encouraging. In real life I find creating a local variable with a different name more natural anyway, e.g:
> 
> // issuesViewController is a property
> // we want to present it, if it can be constructed
> 
> if let to_present = issuesViewController {
>     presentViewController(to_present, animated: true)
> }
> 
> Ilya.
> 
> -- Erica
> 
> 
> 
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
> 
> 
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160202/1087a8dd/attachment.html>


More information about the swift-evolution mailing list