When writing mutating / non-mutating method pairs, one generally implements the mutating version to avoid extraneous allocations, then makes the non-mutating version call it. The bodies of the non-mutating versions are nearly identical, for example see these arithmetic operators of a Matrix implementation:
extension Matrix {
static func + (lhs: Matrix, rhs: Matrix) -> Matrix {
var result = lhs
result += rhs
return result
}
// repeat with - for subtraction
// repeat with .* for elementwise multiplication
// repeat with ./ for elementwise division
// repeat with +, -, *, and / for matrix-scalar arithmetic
}
In an effort to reduce this boilerplate, I spun off a helper function:
func outOfPlace<T, U>(_ t: T, _ f: (inout T, U)->(), _ u: U) -> T {
var result = t
f(&result, u)
return result
}
Which turned the non-mutating operators into one-liners:
extension Matrix {
static func + (lhs: Matrix, rhs: Matrix) -> Matrix { outOfPlace(lhs, +=, rhs) }
static func - (lhs: Matrix, rhs: Matrix) -> Matrix { outOfPlace(lhs, -=, rhs) }
static func .* (lhs: Matrix, rhs: Matrix) -> Matrix { outOfPlace(lhs, .*=, rhs) }
static func ./ (lhs: Matrix, rhs: Matrix) -> Matrix { outOfPlace(lhs, ./=, rhs) }
static func + (lhs: Matrix, rhs: Element) -> Matrix { outOfPlace(lhs, +=, rhs) }
static func - (lhs: Matrix, rhs: Element) -> Matrix { outOfPlace(lhs, -=, rhs) }
static func * (lhs: Matrix, rhs: Element) -> Matrix { outOfPlace(lhs, *=, rhs) }
static func / (lhs: Matrix, rhs: Element) -> Matrix { outOfPlace(lhs, /=, rhs) }
}
That worked quite well, so I attempted to use similar helpers for more things. The unary negation operator seemed like a prime candidate:
static prefix func - (m: Matrix) -> Matrix {
var result = m
result.negate()
return result
}
So I wrote another helper:
func outOfPlace<T>(_ f: (inout T)->()->(), _ t: T) -> T {
var result = t
f(&result)()
return result
}
However, when I tried to call it, there was an error:
static prefix func - (m: Matrix) -> Matrix {
outOfPlace(negate, m)
// error: Partial application of 'mutating' method is not allowed
}
Is there any way around this?
I don’t actually want to partially-apply negate here, I want to call it immediately upon binding a value to it. I would, in fact, prefer if the type of Matrix.negate were (inout Matrix)->() instead of (inout Matrix)->()->().
Are there any plans to allow partial application of mutating methods in the future?
If not, I would like to raise the possibility of flattening the type of unbound mutating methods. This would be entirely source-compatible, since unbound mutating methods are currently disallowed. (We get the same error for “let f = Matrix.negate”.)
In the general case, with more parameters and a return value, the flattened version would work like this:
struct Foo {
mutating func bar(x: Int) -> String {
return "whee"
}
}
let f = Foo.bar // (inout Foo, Int) -> String
var x = Foo()
let w = f(&x, 1) // "whee"
Is this potentially reasonable and feasible?