Why? Have any other languages used this syntax for move? Whereas several languages have used it for assignment.
let b <- a
would be more natural here. but then how best to drop?
_ <- a
nil <- a
unlet a
drop(a)
unbind(a)
remove(a)
_ = move(from: a)
...
Because I want move is more simply to use than copy, but making move default on COW type will be source breaking
Pitching and taking consuming
(née __owned
) through evolution before the move
function would probably clear up a lot of the confusion here.
When Swift can declare move-only values (on the caller side, or as part of the type), that's exactly how the move
or drop
function will work, without any magic, just like the Rust examples. The compiler Builtin
inside move
's implementation will be superfluous in that case. The nice thing about the move
that we're proposing is that it has the same semantics for any argument value. That does require magic to distinguish normal pass-by-value semantics from move semantics.
There are alternatives to using compiler magic to handle copyable argument values. I'm not a syntax afficionado, and I don't see any precedent in the language for something like this, but we should at least consider them:
- A
move
keyword that works as an operator
foo(move arg)
- A
move
parameter modifier
A free-standing move
function could be written as
func move<T>(_ t: move T) -> T {
return t
}
Or even expressed as a prefix operator.
Any user-defined function could force argument moves.
This runs counter to the current Swift practice in which parameter modifiers only affect semantics on the callee-side. Normally, without any recognizable syntax on the caller-side, argument semantics are unaffected. For example __owned
does not currently force a move (nor should it).
consume
is good, but it will be overloaded with other "consuming" uses, which consume their argument value, but do not prevent implicit copies and subsequent uses of the variable. move
consumes its value and also ends the variable lifetime.
Just because something can be spelled a certain way doesnât mean it should be spelled that way. For example, functions can implement the exact same semantics of computed properties. But Swift chooses to give computed properties the same syntax as stored properties because it helps the programmerâs understanding.
I would argue that modeling move
as a function harms the programmerâs understanding. The classical definition of a function is a map from domain to range. In this light, move(_:)
is the identity function. Itâs only when you expand the definition of function
to include its effects on the calling environment that you can even start to explain the semantics of move(_:)
.
As you illustrate, thereâs a good chance that a keyword will be required at the call site to indicate that the client is unbinding the value. Why add the extra layer of function call syntax atop that? Let the keyword do the heavy lifting.
Would it be possible to somehow reference the target binding, rather than using the assignment operator? Assignment isnât whatâs happening, after all. I think thatâs the crux of the issue here.
struct SortedArray {
var values: [String]
init(values: [String]) {
// Ensure that, if `values` is uniquely referenced, it remains so,
// by moving it into `self`
move(\values, to: \self.values) // Syntax of local key path expressions subject to change
// Ensure the values are actually sorted
self.values.sort()
}
}
That would still require a tweak to the grammar to allow local key path expression literals (which need not resolve to a public KeyPath
type for the moment), but I think itâd fit what is actually happening much better: you are operating on bindings, not values.
For that to work, youâd also need some way to make new declarations with let
or var
, ideally while preserving type inference, so obviously the idea needs some work.
When you move something you often want to move it to a function argument, like this:
let something = Something()
prepare(with: move(something)) // avoid making a copy here
A move
function taking a source and destination variable cannot express this.
âŠHuh. Youâre right, short of some even-more-confusing currying nonsense, thatâd be unworkable.
I was also going to suggest a replacement for the assignment operator, but that wouldnât work for function calls either.
That being said, if people are going to be nesting this function in other calls, that makes it even more important that it doesnât look like a normal function.
I found a proposed syntax for move operations in Swift, dating back around a decade ago.
It proposes two operators: an infix <-
for moving a binding to another, and a prefix <-
for use in function calls.
I think this is actually a better approach than using a pseudo-function. Using a prefix for function calls like that also has strong precedent: inout
parameters and pointer conversions use &
.
Not bad.
Another alternative for prefix:
foo(^x)
let y = pop(x)
let y = contentsOf(x)
let y = innardsOf(x)
let y = visceraOf(x)
let y = *x
?
I donât think there is anything wrong with the functionality of move(). Very useful looking tool.
Iâd like to second that both the syntax and name of move(thingyYouWannaMakeUnique) is a little confusing to a person unfamiliar with the SIL. I also believe âmoveâ as a name is a very valuable one for many domains, such as game programming, simulations, etc. Perhaps using a less core english verb or moveSIL etc would be a better choice for the Swift ecosystem, or placing the functionality in a non-function call looking syntax.
I donât have a better suggestion for either, and perhaps thatâd be a fine name and syntax given the high quality intro new features like this can be given in WWDC doc. I donât see that fixing the collision with the highly useful name of âmoveâ for other domains though.
I want to re-emphasize this point. move(_:)
is very likely to be the signature of a method. With implicit self
, how would code inside a sibling method spell the move operation? Builtin.move(_:)
?
Swift.move(_:)
This is no different from a method shadowing any other standard library function.
I remain deeply opposed to making it top-level. It should be inside a caseless enumeration, like the MemoryLayout
functions.
Swift.move(_:)
This is no different from a method shadowing any other standard library function.
Yes, and this strikes me as problematic.
Top-level functions of any kind should be treated as exceptional.
Functions with no type constraints should also be considered exceptional.
Top-level functions with no type constraints in the Standard Library with a word that has myriad domain-specific meanings should be almost unheard of.
This is contradicted by probably the first function that a user learns about in the standard library: print
. There are languages where the analogous facility is namespaced, but Swift is not designed that way. Letâs avoid articulating unsubstantiated âshould beâsâ that are plainly contrary to what the language already is.