Motivation
Right now, there are at least three proposals (SE-0258: Property Delegates - #41 by shpakovski, SE-0253: Static callables, [Accepted] SE-0249: Key Paths Expressions as Functions - #4 by John_McCall) which possibly could be substituted with a more general concept.
I have no illusions that this post will actually lead to a replacement for any of those proposals, and haven't spend much time thinking of the downsides of the idea - but I'd still like to share it, as I'd like to see more discussions with a wider scope (and imho it's more productive than only posting vague complaints and requests for more holistic solutions ;-).
The concept
I hope it's as easy to explain as I think ;-)
The very core of the idea is to allow a type to act as another type.
I really like solid examples, so I'll take the one case where Swift already does the thing I'm talking about:
Wherever Swift expects a parameter of Optional<T>
, it will also accept a value of type T
(this behavior actually has its disadvantages, but overall, it seems to be better than the alternative).
The conversion from T
to Optional<T>
is baked into the language, but it could also be a feature open to be used for other applications.
Strawman syntax
There might be other and better paths, but the most obvious solution for me is an option to "bless" members so that they can directly be accessed in places where only their owning object is written out:
extension Result {
@forward var optional: Success? {
switch self {
case .success(let value):
return value
default:
return nil
}
}
}
// this would allow a Result to be used like an Optional
if let value = methodReturningResult()?.someMemberOfSuccess {
....
The choice of @forward
wouldn't be my favorite, but as this isn't even a pitch, imho there's no need to argue about the color of a bikeshed that doesn't exist ;-)
Applications
Let's look how some examples from other proposals might be implemented with forwarding...
Unwrappable
The example above is basically the implementation of Unwrappable
.
Static callables
struct Adder {
var base: Int
@forward func call(_ x: Int) -> Int {
return base + x
}
}
let add3 = Adder(base: 3)
add3(10) // => 13
Property Delegates
struct Lazy<T> {
private var storage: T?
private var creator: () -> T
init(_ creator: @escaping () -> T) {
self.creator = creator
}
@forward var value: T { // edited
set {
self.storage = newValue
}
mutating get {
if let storage = storage {
return storage
} else {
let value = creator()
self.storage = value
return value
}
}
}
}
var example = Lazy {
return 42
}
let sum = example + 1
print(sum) // 43
Reinventing broken C math ;-)
extension Double {
@forward var float: Float { // edited
get {
return Float(self)
}
set {
self = Double(newValue)
}
}
}
let x = 10 as Double
let y = 20 as Float
let z = y/x // 2 as Float