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": [swift-evolution] Idea for enabling DSLs: bind to self in closures
- "Request for Discussion: Setup closures": [swift-evolution] Request for Discussion: Setup closures
- "Method cascading (was Re: Request for Discussion: Setup closures)": [swift-evolution] Method cascading (was Re: Request for Discussion: Setup closures)
- "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
- [SR-160] Proposal to introduce Method Cascading · Issue #42782 · apple/swift · GitHub
[2] https://kotlinlang.org/docs/reference/lambdas.html#function-literals-with-receiver
[3] GitHub - devxoul/Then: ✨ Super sweet syntactic sugar for Swift initializers
[4] GitHub - Quick/Quick: The Swift (and Objective-C) testing framework.
[5] GitHub - kylef/Spectre: BDD Framework and test runner for Swift projects and playgrounds