[swift-evolution] [Discussion] Here we go again: Extension Functions

Stephen Celis stephen.celis at gmail.com
Thu Feb 25 16:47:48 CST 2016


I'm reopening a topic that has already been discussed here at length[1]: setup closures and `self`-binding closures.

I've been wondering if Swift could adopt what Kotlin calls "extension function expressions"[2]. These would allow us to encode a closure's receiver type (`self`) into the closure type itself, providing a powerful, type-safe way to define DSLs.

Erica's earlier draft proposed this builder pattern:

    with let task = NSTask() {
        launchPath = "/usr/bin/mdfind"
        arguments = ["kMDItemDisplayName == *.playground"]
        standardOutput = pipe
    }

With extension functions, we could approach something similar:

    func with<T>(value: T, body: T.() -> ()) -> T {
        value.body()
        return value
    }

    let task = with(NSTask()) {
        launchPath = "/usr/bin/mdfind"
        arguments = ["kMDItemDisplayName == *.playground"]
        standardOutput = pipe
    }

Or, using the `then`[3] pattern:

    protocol Builder {}
    extension Builder {
        func then(body: Self.() -> ()) -> Self {
            body()
            return self
        }
    }
    extension NSTask: Builder {}
    
    let task = NSTask().then {
        launchPath = "/usr/bin/mdfind"
        arguments = ["kMDItemDisplayName == *.playground"]
        standardOutput = pipe
    }

How about BDD-style frameworks (like Quick/Nimble[4] and Spectre[5])?

    describe("a person") {
        let person = Person(name: "Kyle")

        it("has a name") {
            try expect(person.name) == "Kyle"
        }
    }

And HTML builders?

    html {
        head { title("Hello, World!") }
    }

And block-based, transactional APIs?

    db.inTransaction {
        delete("users", "first_name = ?", ["Jake"])
    }

The main benefits have been previously discussed:

1. The ability to remove noise ("$0" everywhere)
2. The ability to avoid defining globals (in favor of a safer, scoped interface)

Would such an enhancement be feasible?

---
Stephen

Footnotes:

[1] A probably-incomplete list:
- "Idea for enabling DSLs: bind to self in closures": https://lists.swift.org/pipermail/swift-evolution/2015-December/000114.html
- "Request for Discussion: Setup closures": https://lists.swift.org/pipermail/swift-evolution/2015-December/000211.html
- "Method cascading (was Re: Request for Discussion: Setup closures)": https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151130/000729.html
- "Fluent syntax (replacing void with a useful default return value)": http://thread.gmane.org/gmane.comp.lang.swift.evolution/374
- "Lexical scope statement (with .. do)": http://thread.gmane.org/gmane.comp.lang.swift.evolution/1408
- "Scoped resources (like C# using statement)": http://thread.gmane.org/gmane.comp.lang.swift.evolution/1641
- "Customized Inline Init Closure": http://thread.gmane.org/gmane.comp.lang.swift.evolution/1946
- "Then Support": http://thread.gmane.org/gmane.comp.lang.swift.evolution/2054
- "Draft proposal: multi-property assignment .= operator": http://thread.gmane.org/gmane.comp.lang.swift.evolution/2960
- "Custom default names for arguments of closures": http://thread.gmane.org/gmane.comp.lang.swift.evolution/6969
- https://bugs.swift.org/browse/SR-160

[2] https://kotlinlang.org/docs/reference/lambdas.html#function-literals-with-receiver
[3] https://github.com/devxoul/Then
[4] https://github.com/Quick/Quick
[5] https://github.com/kylef/Spectre



More information about the swift-evolution mailing list