Agreed that the searchability would still be poor in either approach, but a keyword would be better than a symbol - not that we should base all our decisions on the whims of search-engine algorithms, but it’s far easier to require a specific word in search results than it is a specific symbol, especially if that symbol isn’t separated from other words by whitespace.
If anything, this is the argument against ~
, in favour of another symbol like !
or -
.
We don't want it to be perceived like a destructor.
Well, I actually don't know about how English people feel about keywords' searchablity because I use google in Japanese. But you can search "swift ~ operator" on google (that's how typical Japanese search about bitwise not operator), and find literally nothing appears. Perhaps the same happens for ~Copyable notation.
If it's a keyword, you can get some results, even if they are common words like any, some or for.
I’m curious about what you mean by “virality of move
”. Do you mean the ubiquitous usage of these features across programming domains, despite the core team’s stated assumption that noncopyable types are a niche feature intended for only a couple key use cases?
This concept is the reason I’ve been using up space on this thread to voice my opinions despite not being someone who plans to use noncopyable types in the niche way anytime soon. I’m intrigued by the possibility of more common use cases for noncopyable types (for example, I posted this recently), and I’m worried that the syntax/feature design might be being overly influenced by the assumption that very few people will find it useful.
This idea interests me a lot. Does this mean that it can make sense to use consume
on a copyable type? Because it seems possible to me that this would be the actual way to use these new features in order to replace some classes, without actually needing to use types that are themselves noncopyable.
Not only that, but single-character prefix operators are inherently hard to notice while scanning code. It's really easy to miss the difference between condition
and !condition
while reading through a file. I worry that ~Copyable
will suffer from the same issue.
I find ~Copyable
to be extremely un-readable, and code is meant to be read.
Another option for a word prefix version: sans Copyable
We rarely do a vote on this forum, maybe we can do it in this case?
The most fair vote system is a bit complicated (where you have N - 1 rounds for N choices) but perhaps we could use the built-in discourse voting plaque as a mean to get to a lower N. For starters it could be word
vs special symbol
. The final vote result would not automatically mean a winner, but there could be a strong support for one of the options in which case it would be a good indicator of what we want.
That is true, but at the same time, I think we also want libraries to include comprehensive support for non-copyable types. Otherwise having non-copyable data would be extremely inconvenient, as you wouldn't be able pass it to any generic algorithms.
So I think we should also expect that "copyability is optional" is a constraint that will appear rather often in interfaces that are exposed to Swift developers of all levels.
We've said this before, but swift-evolution is not a democracy. The language workgroup is mostly not interested in how many people like each option--we're interested in the reasons that people use to argue for them. A single person who clearly states a compelling argument will lead us to reject a popular suggestion. We are the court, you are the lawyers arguing your cases. The lawyers don't get to vote.
"compelling argument" is highly subjective thing, e.g. the readability of ~
was brought above which some would consider compelling and others won't. If not for us mortals there must be some voting mechanism for the jury / court members themselves as there could easily be a split of opinions there.
Which is? We don't know what it is... or do we? ~
is brought up frequently but there's a bias towards it because it is the symbol that's used in the proposal. So what is the "vox populi"?
I didn't mean to imply that there's a "popular suggestion" in this case. I mean that, in multiple proposals in the past, many of people have said "I like X" but we ultimately rejected X because one person clearly laid out "Y would be better than X because ..." or "we shouldn't do X because it makes the following ambiguous..."
There's an element of subjectivity in the choice of spelling here, but there might also be real tradeoffs. "I don't like ~
" doesn't give us much useful information. "I prefer ?
because it implies ..." is better. "We should reserve ~
for this other purpose" or "We shouldn't use ~
because it creates a parsing ambiguity with this feature we might want in the future" are much better still.
I do not expect that we will be able to make a final decision solely on the basis of such non-subjective considerations. But "evolution focused their attention on this issue, and ultimately there was no objective argument for any one option" is also useful. It tells the workgroup that we can pick the one that we can make a choice based on our taste and that's OK, to the best of our information. If evolution manages to rule out one or two options, that's even better.
If not for us mortals there must be some voting mechanism for the jury / court members themselves as there could easily be a split of opinions there.
Yes, ultimately the workgroup will vote an pick an option (though we try pretty hard to reach consensus before we resort to voting). What I am saying is that our votes are influenced much more strongly by your reasons than they are by your preferences, so if you do have strong preferences, your effort is best spent on clarifying why.
This idea interests me a lot. Does this mean that it can make sense to use
consume
on a copyable type?
Absolutely. I personally predict I will use move-only types most frequently as a wrapper for copyable types, as part of guaranteeing an implementation of a data structure avoids copies. Wrapping will involve consuming the copyable value.
if we care about searchability, we should absolutely avoid -
as a sigil, since that excludes any results that include "Copyable".
Absolutely. I personally predict I will use move-only types most frequently as a wrapper for copyable types, as part of guaranteeing an implementation of a data structure avoids copies. Wrapping will involve consuming the copyable value.
You should never need to do this, except maybe in the extreme short term, since we plan to make it so that borrow bindings are not implicitly copyable even when bound to values of copyable type.
I’m talking about something like this:
struct Cell<Wrapped: ~Copyable> : ~Copyable {
var value: Wrapped
init(wrapping value: consuming Wrapped) {
self.value = move value
}
consuming func unwrap() -> Wrapped { value }
}
IIUC, init
must consume value
rather than borrow it because the lifetime of the Cell
is unbounded.
Also I’d like to point out how confusing it is that ~Copyable
means two different things on the same line. I think I it’s important that the generic constraint and the type declaration modifier have different spellings, and my brain really wants to see the modifier spelled moveonly struct
.
I think we specifically don't want them to be such a core part of most programmer's experience with the language.
I had similar thinking on generics ca. 2015. Now I start teaching generics the first day of class. Given that Rust seems to have embraced non-copyable types as one of the first things you are taught as part of it being a "safe" language, I sort of suspect that in a couple of years I'll be deferring the teaching of ARC to very late in the course as a sort of a "you-only-need-to-know-this-if-you-end-up-in-a-place-with-an-old-codebase" option. I'm leaning strongly that way at the moment.
I'm curious as to what you feel makes them something to be avoided by most programmer's @John_McCall? Sure there will be a learning curve but they certainly don't seem worse than Concurrency and we're making everyone face that feature now.
I’m talking about something like this:
struct Cell<Wrapped: ~Copyable> : ~Copyable { var value: Wrapped init(wrapping value: consuming Wrapped) { self.value = move value } consuming func unwrap() -> Wrapped { value } }
IIUC,
init
must consumevalue
rather than borrow it because the lifetime of theCell
is unbounded.
Right. What I was saying was, if your only reason to define this type is to prevent a normally copyable value from being implicitly copyable, there will be better ways to do that soon.
Also I’d like to point out how confusing it is that
~Copyable
means two different things on the same line. I think I it’s important that the generic constraint and the type declaration modifier have different spellings, and my brain really wants to see the modifier spelledmoveonly struct
.
They don't really mean different things, though. In both cases ~Copyable
means "not assumed to be copyable here (but may be copyable somewhere else)". Whether or not there is a "somewhere else" that makes the thing strictly non-copyable is not really important enough in the abstract to need a separate syntax for any reason other than natural language clarity, which is a reasonable consideration, but introducing artificial syntactic distinctions has its own tradeoffs.
if we care about searchability, we should absolutely avoid
-
as a sigil, since that excludes any results that include "Copyable".
There is approximately a 0% chance that any introductory article on Copyable
would not also discuss ~Copyable
/-Copyable
, however it is spelt.
If we’re thinking ahead to how this syntax might be extended to other protocols/layout constraints down the road, then I certainly believe it’s worth considering how searchable the more general form will be, absent any specific ties to Copyable
.
… the workgroup is willing to consider alternatives of the form
xCopyable
, wherex
is a printed ASCII character available on US keyboards that doesn't obviously mean something else.
May I suggest the non-
prefix, instead of a single character? For example:
struct FileDescriptor: non-Copyable {
private var fd: Int32
}
Right. What I was saying was, if your only reason to define this type is to prevent a normally copyable value from being implicitly copyable, there will be better ways to do that soon.
I’m not sure the alternative techniques will be applicable in all situations. If you need to sequester a value for an indeterminate period of time—for example, if you’re implementing Pin
or a custom allocator (my expected case)—then I don’t see how borrows help, as they are intrinsically scoped.