[swift-evolution] Request for Discussion: Setup closures

Gaelan Bright Steele gbs at canishe.com
Fri Dec 4 18:45:45 CST 2015


JavaScript had a `with` operator for a while, but it was deprecated due to some edge cases, specifically something like this:

    // Codebase 1 (a library)
    class A {}
    internal func foo() {
        print("correct result")
    }
    func doSomethingWithA(a: A) {
        with a {
            /* do something with A */
            foo()
            /* do something else with A */
        }
    }
    // Codebase 2 (Application code depending on CB1)
    class B: A {
        // presumably written without knowledge of the global foo
        func foo() {
            print("completely different result”)
        }
    }
    doSomethingWithA(B()) //What happens here? Does the foo() call the global foo or B’s foo?

I think that in a static programming language we could avoid this problem by having the `with` operator use the knowledge of the type it is passed at compile-time (ignoring the runtime type). Still, we need to be careful about edge cases here.
> On Dec 4, 2015, at 1:35 PM, Sean Heber <sean at fifthace.com> wrote:
> 
>> 
>> On Dec 4, 2015, at 3:27 PM, Joe Groff <jgroff at apple.com <mailto: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
> 
> _______________________________________________
> 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/20151204/4e8c8b21/attachment-0001.html>


More information about the swift-evolution mailing list