Unbound reference to mutating method

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?

2 Likes
Terms of Service

Privacy Policy

Cookie Policy