[swift-evolution] Proposal: helpers for initializing properties of same name as parameters

Matthew Johnson matthew at anandabits.com
Fri Dec 4 21:59:21 CST 2015


I think this idea scratches at the surface of a problem with initializers that definitely merits some attention.  Boilerplate is common in initializers which can lead to types that don’t allow as much flexibility to callers as might be desirable.  

More importantly, it can also lead to “initialized” instances that are not fully or properly configured, (implicitly unwrapped?) optional members, and mutability that shouldn’t be necessary past the initial configuration stage of the instance.  For example, if a type provides a family of initializers, but also has several members which are intended to be initialized / configured directly by callers a developer could choose to avoid the boilerplate by declaring the additional members as as an implicitly unwrapped optional var members.  No doubt this is a bad idea.  If the caller does not initialize all of the additional members there is a bomb waiting to go off.  Furthermore, there is the potential for mutation after initialization that may not be expected or intended.  By requiring a nontrivial amount of boilerplate to avoid this situation the language is unintentionally nudging lazy developers towards bad practices like this.

Let's start with the current proposal but go a bit further and see how much boilerplate can be removed.  The type information is already known from the property declaration, and furthermore a default may also be available in the property declaration.

struct Foo {
    let bar: String
    let bas: Int = 1
    let bat:  Float = 0
    let bax: String = “default"
    let baz: Double
    
    // self.bar is known to be a String but does not have a default value so it must be provided by the caller  
    // self.bas is known to be an Int and implicitly has a default value of 1 specified by the property 
    // so it does not need to be provided by the caller
    // self.bat is known to be a Float, but the default is overridden to be 1 instead of 0
    // external labels are provided that correspond to the names of the properties
    init(self.bar, self.bas, self.bat = 1) {
	// the initializer does not receive a value for baz and the property does not provide a default 
        // so it must be initialized here before the first phase of initialization is complete
        // if all stored properties had received values through parameters or through property defaults 
        // the first phase of initialization would have already been completed when the body of the initializer was entered
        self.baz = someComputedDoubleValue()
        // now all stored properties have been initialized so the first phase of initialization is complete
    }
}

This structure allows us to remove the boilerplate of the property type and default value.  

Given the significant difference from regular function parameters, it may make sense to set these apart syntactically in some way, although I am not sure what would make the most sense.  For example, we could just have a second parameter tuple in the initializer declaration which represents the “member parameters” to the initializer like this:

init(foo: Int)(self.bar, self.bas, self.bat = 1)

or a vertical pipe like this:

init(foo: Int | self.bar, self.bas, self.bat = 1)

Setting these “member parameters” apart syntactically could also facilitate additional mechanisms to further reduce boilerplate if we find that the same group of “member parameters" exist for several different initializers in the same type.  For example, it might become possible to implement a memberwise initializer like this:

// @automembers expands to match all stored properties
init()(@automembers) {}

At this point we have eliminated virtually all of the boilerplate, encouraging developers to use concise *and* safe, yet still flexible initialization strategies.  

Matthew



> On Dec 4, 2015, at 9:05 PM, Dan Appel <dan.appel00 at gmail.com> wrote:
> 
> I'm not sure how I feel about this, but in either case, why limit it to default parameters? It's useful in other situations, too.
> 
> Take this for example:
> struct Foo {
>     let bar: String
>     let bas: Int
>     let baz: Double
>     init(self.bar: String, self.bas: Int, bax: Int) {
>         self.baz = Double(bax)
>     }
> }
> 
> where the need to say
> 
> self.bar = bar
> self.bas = bas
> 
> is now avoided.
> 
> On Fri, Dec 4, 2015 at 3:16 PM Tal Atlas <me at tal.by <mailto:me at tal.by>> wrote:
> There’s lots of boilerplate of initializing structs with stored properties and initializer parameters. I’d like to create a syntax for alleviating that in the 90% case.
> 
> ```swift
> struct Foo {
>     let bar: String
>     let baz: Int
> 
>     init(self.bar: String = "default", counter self.baz: Int) {
>     }
> }
> ```
> 
> This would be identical to:
> ```swift
> struct Foo {
>     let bar: String
>     let baz: Int
> 
>     init(bar: String = "default", counter baz: Int) {
>         self.bar = bar
>         self.baz = baz
>     }
> }
> ```
>  _______________________________________________
> 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>
> -- 
> Dan Appel
>  _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20151204/a1c79ea3/attachment.html>


More information about the swift-evolution mailing list