4 ms·
In Swift, += is a separate operator from +, and is defined within the confines of the language. It's not privileged in any particular way: func +=(inout lh
by ridiculous_fish 11y ago
In Swift, += is a separate operator from +, and is defined within the confines of the language. It's not privileged in any particular way:
func +=(inout lhs: Int, rhs: Int)
and you can just implement this.
You might object that special forms are still privileged, e.g. foo[idx]+=1. But in fact these are not special: one of the surprising features of Swift is that inouts work on computed properties. For example, say we add a mutable property highHalf on UInt64:
extension UInt64 {
var highHalf : UInt32 {
get { return UInt32(self >> 32) }
set { self = (self & 0xFFFFFFFF) | (UInt64(newValue) << 32) }
}
}
Now you can do this:
var x = UInt64.max
x.highHalf -= 1
So complex references can be used for any inout. That's how array indexing works.
Swift is the first language I've encountered that works this way; are there others?
- ScottBurson 11y ago> Swift is the first language I've encountered that works this way; are there others? Lisp, of course :-) Common Lisp's SETF and related macros work this way. E.g., (incf (ldb (byte 3 5) x)) would increment the "byte" (CL's term for an arbitrary bit field) of width 3 at offset 5 of X. INCF is defined in terms of SETF. Of course, the set of macros that can update a place is user-extensible. So if you wanted one that multiplies the value in the place by two, you could just write something like this (the trailing "f" in the name, as you might guess, is the convention for such a macro): (define-modify-macro doublef () (lambda (x) (* x 2))) then you could call it like this: (doublef (ldb (byte 3 5) x)) to double the value in the bit field. (EDIT: used lambda expression instead of helper function.)
- jonhohle 11y agoIsn't this how C++ works as well? operator+= is definable for any user defined types and typically you'll add an overloaded operator[] which returns a reference so that things like operator+= works as you'd expect within the context of a collection. I have some old code somewhere for images which I'm sure does something to the effect of: image[x, y] *= scale; where Image::operator[] returns a Pixel&.
- ridiculous_fish 11y agoNot quite. C++'s references are syntactic sugar on top of pointers: a Foo& always needs a Foo to point to. But Swift's inouts are implemented as a pair of closures: a getter and a setter, which need not just modify a Foo. Consider the much-hated vector<bool>, implemented as a packed bit array. Since you can't return a reference to a bit, operator[] must create a special wrapper class that holds a reference to the word and the bit offset, and then overload operator= and perform other tricks. It's famously leaky and gross. In Swift, we can implement this optimization much more cleanly. inout lifetimes are always scoped, and when the scope ends, the new value is written back to the object via its setter. Our `inout boolean` is not backed by a boolean at all: it's backed by a getter and setter which can do arbitrary things, like mask and shift a word.