[swift-evolution] Request for Discussion: Setup closures
Sean Heber
sean at fifthace.com
Fri Dec 4 15:35:02 CST 2015
> On Dec 4, 2015, at 3:27 PM, Joe Groff <jgroff at apple.com> wrote:
>
>
>> On Dec 4, 2015, at 1:24 PM, Erica Sadun <erica at ericasadun.com <mailto:erica at ericasadun.com>> wrote:
>>
>> PROBLEM: With many Apple-supplied classes, typical initializers fail to fully set up an instance for use. Here's one example:
>>
>> let task = NSTask()
>> task.launchPath = "/usr/bin/mdfind"
>> task.arguments = ["kMDItemDisplayName == *.playground"]
>> task.standardOutput = pipe
>>
>> Here's another:
>>
>> let questionLabel = UILabel()
>> questionLabel.textAlignment = .Center
>> questionLabel.font = UIFont(name:"DnealianManuscript", size: 72)
>> questionLabel.text = currentQuestion.questionText
>> questionLabel.numberOfLines = 0
>>
>> You end up with stodgy repetitive code that turns into a blocky hard-to-follow clump. Here are some of my complaints:
>> This code feels unnecessarily redundant
>> This code visually stacks. The task/task/task and questionLabel/questionLabel/questionLabel blocks draw attention away from the actual set-up these lines of code are intended to do.
>> The extra symbol verbiage goes against common Swift style. For example, when the context is clear, prefer .whitespaceAndNewlineCharacterSet to NSCharacterSet.whitespaceAndNewlineCharacterSet.
>> Further, if you have many instances to set up there's no clear way to differentiate unrelated set-up groups other than inserting whitespace gaps or building custom factory functions.
>>
>> PROPOSED SOLUTION: What do you think about creating setup closures that modify initializers and automatically introduce self-references. For example the NSTask() initialization might look something like this instead:
>>
>> let task = NSTask()>>{
>> launchPath = "/usr/bin/mdfind"
>> arguments = ["kMDItemDisplayName == *.playground"]
>> standardOutput = pipe
>> }
>>
>> In this example, the braces are scoped to the instance as self, enabling the properties to entirely drop their prefixes and be grouped together for set-up.
>
> In Smalltalk and Dart, you can do this with method cascades, which apply multiple methods to the same 'self'. In Dart they use '..' for this:
>
> let task = NSTask()
> ..launchPath = "..."
> ..arguments = [...]
> ..standardOutput = pipe
>
> The nice thing about that is that it's a bit more generally applicable than just initialization.
>
> -Joe
I would also be in favor of something more generally applicable rather than making initialization more special. I think I prefer the block-style over Dart’s approach. Smalltalk uses semicolons for this, if I recall. I’m not sure I like using a special operator. I’m obviously a keyword guy.. :P
with let task = NSTask() {
launchPath = "/usr/bin/mdfind"
arguments = ["kMDItemDisplayName == *.playground"]
standardOutput = pipe
}
Also valid:
with someVariable {
func1()
func2(“etc")
}
Which would call func1() and func2() on the someVariable instance (which I think would be quite expected).
l8r
Sean
More information about the swift-evolution
mailing list