[Proposal] Allow optional binding of instance variables in constructors


(Taras Zakharko) #1

One place where I find myself using the guard statement a lot is state initialisation. E.g. in init()

guard let
           device = MTLCreateSystemDefaultDevice(),
           library = device.newDefaultLibrary()
else {
   fatalError(“Could not initiallize Metal, aborting”)
}

Here, the two variables device and library are intended to be instance variables of an engine backbone system. However, because optional binding in guard can only introduce new local statements, one needs to add additional boilerplate in the end, e.g.:

self.device = device
self.library = library

What I want to propose is a very simple QOL enhancement: allow optional binding to be used with instance variables in the constructors. If the binding fails, the instance construction cannot proceed (the relevant scope is the instance). The syntax would be

guard device = MTLCreateSystemDefaultDevice(),
           library = device.newDefaultLibrary()
else {
   fatalError(“Could not initiallize Metal, aborting”) // or return nil
}

Few notes:

- This would only apply to guard statement, because binding in the if statement only applies to the internal if scope by definition
- One could also ask for instance optional binding in any context where instance assignment is legal. However, this would make guard inconsistent, as it won’t be able to prevent inconsistent state from being invisible to the app. Therefore its best to restrict this to cases where state is being constructed, and fail the construction if the guard fails (hence init() )

Best,

Taras


(Jacob Bandes-Storch) #2

Clarifying questions as comments:

    guard device = MTLCreateSystemDefaultDevice() else {
        fatalError()
    }
    device = nil *// Is this still allowed here?*
    device.newDefaultLibrary() *// Is this allowed without optional
chaining?*

Also, I don't see any reason this idea should be restricted to instance
variables. It could work for any variable, I think, but in any case it gets
tricky if the value is mutated again afterwards.

Jacob

···

On Mon, Apr 11, 2016 at 12:31 AM, Taras Zakharko via swift-evolution < swift-evolution@swift.org> wrote:

One place where I find myself using the guard statement a lot is state
initialisation. E.g. in init()

guard let
           device = MTLCreateSystemDefaultDevice(),
           library = device.newDefaultLibrary()
else {
   fatalError(“Could not initiallize Metal, aborting”)
}

Here, the two variables device and library are intended to be instance
variables of an engine backbone system. However, because optional binding
in guard can only introduce new local statements, one needs to add
additional boilerplate in the end, e.g.:

self.device = device
self.library = library

What I want to propose is a very simple QOL enhancement: allow optional
binding to be used with instance variables in the constructors. If the
binding fails, the instance construction cannot proceed (the relevant scope
is the instance). The syntax would be

guard device = MTLCreateSystemDefaultDevice(),
           library = device.newDefaultLibrary()
else {
   fatalError(“Could not initiallize Metal, aborting”) // or return nil
}

Few notes:

- This would only apply to guard statement, because binding in the if
statement only applies to the internal if scope by definition
- One could also ask for instance optional binding in any context where
instance assignment is legal. However, this would make guard inconsistent,
as it won’t be able to prevent inconsistent state from being invisible to
the app. Therefore its best to restrict this to cases where state is being
constructed, and fail the construction if the guard fails (hence init() )

Best,

Taras

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Taras Zakharko) #3

Clarifying questions as comments:

    guard device = MTLCreateSystemDefaultDevice() else {
        fatalError()
    }
    device = nil // Is this still allowed here?

Device instance variable is not an optional — so, no. The entire point of this would be to init potentially failable substate without having to deal with forcibly unwrapped optionals or having guards every time when substate needs to be accessed etc.

    device.newDefaultLibrary() // Is this allowed without optional chaining?

Sure

Also, I don't see any reason this idea should be restricted to instance variables. It could work for any variable, I think, but in any case it gets tricky if the value is mutated again afterwards.

The problem as I see it is that guard enforces state consistency. If the guard fails, any code that access the guarded state becomes invalid. This is why guard else clause requires one to leave the enclosing scope — to ensure that the guarded variables cannot be accesses in valid code. If one extends the binding to other variables, one runs into the problem that these variables can be accessed from outside. This is why I am suggesting to restrict guard to constructors only and fail the object construction if the guard fails.

— Taras

···

On 11 Apr 2016, at 10:05, Jacob Bandes-Storch <jtbandes@gmail.com> wrote:

Jacob

On Mon, Apr 11, 2016 at 12:31 AM, Taras Zakharko via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
One place where I find myself using the guard statement a lot is state initialisation. E.g. in init()

guard let
           device = MTLCreateSystemDefaultDevice(),
           library = device.newDefaultLibrary()
else {
   fatalError(“Could not initiallize Metal, aborting”)
}

Here, the two variables device and library are intended to be instance variables of an engine backbone system. However, because optional binding in guard can only introduce new local statements, one needs to add additional boilerplate in the end, e.g.:

self.device = device
self.library = library

What I want to propose is a very simple QOL enhancement: allow optional binding to be used with instance variables in the constructors. If the binding fails, the instance construction cannot proceed (the relevant scope is the instance). The syntax would be

guard device = MTLCreateSystemDefaultDevice(),
           library = device.newDefaultLibrary()
else {
   fatalError(“Could not initiallize Metal, aborting”) // or return nil
}

Few notes:

- This would only apply to guard statement, because binding in the if statement only applies to the internal if scope by definition
- One could also ask for instance optional binding in any context where instance assignment is legal. However, this would make guard inconsistent, as it won’t be able to prevent inconsistent state from being invisible to the app. Therefore its best to restrict this to cases where state is being constructed, and fail the construction if the guard fails (hence init() )

Best,

Taras

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution