Syntactic sugar for assignment precondition: x = a -> b


(Amir Michail) #1

One would write x = a -> b to make x change its value from a to b. This is syntactic sugar for:

precondition( x == a )
x = b

This pattern comes up a lot when you want to increase your confidence that your code is doing what you think it should be doing.


(Adrian Zubarev) #2

I don't buy it, it's too cryptic from my point of view. You can easily create a simple function that does everything you want in one line and is far more readable, so -1 for me.

func change<T>(_ value: inout T, from oldValue: T, to newValue: T) {
  precondition(value == oldValue)
  value = newValue
}

// usage
change(x, from: a, to: b)

(Steve Canon) #3

I agree with @DevAndArtist that this probably doesn't rise to the level of needing an operator. There's a widely-used term of art for this operation: "compare-and-swap". I would expect expect any library implementation to use a variant of that name.

(I would also expect it to not have a precondition, but return either Bool or Optional<T> indicating whether the check succeeded--and the new value in the case of an Optional--which the caller could then handle by a fatalError if they really want the precondition behavior or more likely do something else.)


(Amir Michail) #4

The -> operator as defined above would encourage you to use this sort of assignment more frequently as it would have minimal impact on the readability of your code.


(Davide De Franceschi) #5

Not exactly the requested syntax, but you can do something like

struct Update<Value> {
	var from: Value
	var to: Value
}

infix operator =>

func => <Value> (lhs: Value, rhs: Value) -> Update<Value> {
	return Update(from: lhs, to: rhs)
}

struct InvalidUpdateError<Value>: Error where Value: Equatable {
	var value: Value
	var update: Update<Value>
}

infix operator != : AssignmentPrecedence

func != <Value> (lhs: inout Value, rhs: Update<Value>) throws where Value: Equatable {
	guard lhs == rhs.from else { throw InvalidUpdateError(value: lhs, update: rhs) }
	lhs = rhs.to
}

var foo: Int = 3
try foo != 3 => 4
print(foo) // 4