I don't know anything about Apple Silicon so I'm afraid I can't say anything concrete here, but I can wildly speculate like any other industry observer :-) I would guess that they added atomic memory operations that have looser memory consistency model than the default ARM model (and certainly better than the X86 model), more aligned with the RISC-V relaxed consistency atomics. iPhones have had more efficient atomics than Intel processors for a long time AFAIK.
That said, I'm far from an expert on this and have no insider knowledge, I expect it will become more public as the hardware becomes more available.
I haven't had time to engage with this thread yet, but let me just pop in to note that "semantics" is not a plural noun and cannot have its s removed. While "semantic" is indeed an adjective, it does not mean "having (these) semantics" but "related to or expressing meaning", and "value-semantic" is not in use.
If that argument carries the day, we could call it AnyValue to be consistent with AnyObject. Given that AnyObject conformance is implicit, this is especially interesting if we go with implicit conformances to AnyValue.
I’ve been turning it over in my head and I disagree that ‘value-semantic’ is incorrect as an adjective. I think the best analogy is to a well-established term like ‘set-theoretic,’ meaning something like ‘related to set theory.’ Note that the term is not (or at least, not as commonly) ‘set-theoretical,’ even though the adjective from ‘theory’ is usually ‘theoretical.’ This suggests to me that ‘set-theoretic’ is best thought of as the adjective derived directly from the term ‘set theory,’ rather than being derived directly from ‘theoretical’ with ‘set’ tacked on the front. Similarly, it seems quite reasonable to derive an adjective from the noun phrase ‘value semantics’—and what else would it be, but ‘value-semantic’? Yes, the meaning is different than the simple composition of the meanings of ‘value’ and ‘semantic,’ but that’s just as ‘set-theoretic’ means something a little different than just ‘theoretical and related to sets.’
I also haven't followed all of this thread, but any way to make this implicit would be nice. Otherwise pretty-much every wrapper will need to add conditional conformances to indicate that they preserve the value-ness of the thing they wrap:
extension Optional: ValueSemantics where Wrapped: ValueSemantics {}
extension Slice: ValueSemantics where Base: ValueSemantics {}
... etc
LazyMapCollection and LazyFilterCollection may or may not have value semantics depending on the map/filter closure.
Setting aside existing non-salient API for the sake of discussion, is this something that should be addressed only through documentation? Or should there be a naming convention as there is with unsafe? Salience is important enough that it seems like a convention could be justified. That might make it easier to teach and would certainly make non-salience clear when reading and writing code.
FWIW, my sense is that non-salient attributes are relatively rare, and don't (in and of themselves) create a safety problem, so don't warrant a special naming convention.
Thanks, John. I was never too comfortable with the name anyway, not least for the reason cited by @Karl:
IMO Value would be a much better name, though I expect it might clash inconveniently with commonly-used associated type names, and might also cause some confusion when discussing the language-level concept of value, which is related but not identical. We could also (ab)use the name Regular, giving it a slightly different meaning from its classical definition on language-specific grounds, but SemiRegular is a closer match as a precedent.
Thanks; that reasoning is good enough for me! Since we may not be able to drop Semantic[s] from the name for practical reasons, it's enough to keep “ValueSemantic” on my table.
I'm truly sorry I missed this thread back in November since Swift value semantics is one of my favorite topics. I thought about it back in 2016 and ended up writing a book chapter on it (in The Swift Apprentice) and also giving a talk on the topic. So it was uncanny to discover this thread, which seems to have converged on basically the same definition I settled on but with some interesting differences in emphasis.
I know it's a bit late now but I thought it might be interesting to point out some of those commonalities and differences, since I reached the same point by a different road and it can be fun to see the same view from a different angle.
First, the core conceptual definition is the same. I had settled on the view that "a type has value semantics, if the only way to modify a variable's value is through the variable itself." This seems to match the key parts of this thread's definition, that "safe code not being able to "observe the value of a except via an expression that uses a," andd that "safe code not being able to "alter the value of a" except via a explicit list of mechanisms. (As far as I can tell, all these mechanisms also involve expressions that use a.)
Another point of commonality is a kind of recursive structural test of whether a type is value semantic, based on whether its stored properties have value semantics. The thread names these as corollaries of the definition:
that "a let-bound instance is constant for all time"
that if a value type's stored properties all have value semantics, then that value type will have value semantics.
I ended up describing similar points in terms of an equivalent, constructive definition, a recipe for value semantics. You can apply this recipe either to build a value semantic type by cookbook, or else to analyze a type to determine if it has value semantics. The recipe works like this:
primitive value types (like Int) are value semantic by nature
a reference type is value semantic if it is immutable, which will be the case if its stored properties are let-bound and are themselves value semantic types.
a value type is value semantic if its stored properties are all value semantic, or else if it handles CoW correctly (for which I describe a further recipe, and also note that VS is always defined with respect to a particular access level)
As far as I can tell, this is still a correct procedure.
What's maybe more interesting is a few differences in emphasis.
This thread's definition is more explicit about the "independent notional value", implied by salient properties or basis operations. I merrily dodged the issue in a footnote by saying value is what you're trying to test with Equatable. But on this topic, I noted (what I still believe is true) that value semantics can be defined without any notion of an instance at all, that this is part of the valuable simplification it provides, and that much of the confusion in this area is likely due to the historical tension between machine-oriented and mathematicaly-oriented definitions of value (e.g., C vs SML).
I emphasized the point that a reference type can be a value semantic type, as a consequence of the natural definition, and argued that "value type"-vs-"reference type" is an implementation detail, while "value semantic"-vs-not is essentially a key part of a type's interface.
I also offered an equivalent adversarial definition in terms of "the mutation game," an imagined fight between Victor the Valucist and a Salazar the SideEffector, to test if a type is immune from side effects. This strikes me as useful for intuition and teaching.
The definition in this thread states that part of what is meant by value semantics is that "concurrent access to the values of distinct variables of type X cannot cause a data race," and that concurrency properties are emphasized more generally. I can see that needs to be stated explicitly, since it doesn't follow automatically from other points.
Anyway, I hope it is not too wildly self-indulgent for me to retread this, as it's all old news now! But if anyone is still curious, you can still see my old talk online.
Not at all; very happy to see more interest in this.
It's not quite equivalent, I think…
What's maybe more interesting is a few differences in emphasis.
This thread's definition is more explicit about the " independent notional value ",
I think we need the idea of notional value to define value semantics rigorously. There are a few holes in your definition, IMO, that I intended to close by introducing this idea:
• There are no primitive types in Swift, so you don't really have a meaningful basis case. Is AnyObject a primitive type?
• Operations on a value type can access mutable globals, and can present the values of those globals as though they are part of its value. I could make an empty struct with exactly the same API surface as Int, and it would not have value semantics in the sense we mean the term.
In the general case, whether a type has value semantics is not a property that can be derived from the code that defines the type; it's a property of the programming model presented by the type to its clients.
value semantics can be defined without any notion of an instance at all
Sorry, I don't know what point you mean to make here; the definition I offered does not mention the word “instance.”
I collaborated on a research paper about this recently, and I can say we studiously avoided that word because its meaning is fuzzy at best. That's one of the reasons my definition mentions variables.
I think what @algal describes here covers exactly value semantics.
With Value Semantics, the mapping between variable and value becomes n to 1 such that a variable is nothing more than a local constant representing some value, mutating that value would rebind the variable, we can even state here that we create a new variable with the same name.
Which is however different for Non Value Semantics, where we have a m to n relationship between value and variable, because a variable isn't anymore an alias to a value. Instead, it is an alias to some resource holding an instance which represents a value.
The resource is some abstract location able to represent values of some type with representatives/instances, it would mostly map to a memory location in the backend.
The instance/representative is either sides some kind of value which can be mapped to a value inside the type system, it would be a binary sequence in most backends.
I think projecting value semantics to types is a failure, if we were allowed to pass structs by plain reference (not inout) then we would undermine the concept of "Value Semantics" for Types.
A type shouldn't provide value semantics, instead variables should do that with appropriate annotations/keywords.
Unfortunately, one cannot describe value semantics solely in terms of whether or not one is able to create an alias onto some memory location. Values of types having value semantics compose in a way other values do not.
When we think about aggregates, value semantics must be understood as a property of the programming model, because one cannot determine whether a property is a part of the aggregate, or a mere association. In a typed language, it makes sense that this property be associated with types, as types are the abstraction we use to talk about values, statically.
Here is an example:
class Vec2 { var x, y: Int }
struct Rect { var width, height: Vec2 }
Whether or not width and height are part of Rect is not clear from the code alone. One can imagine these properties are intended to be shared between different shapes (i.e., they represent an observer/observed relationship), or that they are truly part of the a (i.e., they represent a whole/part relationship), yet their type must be implemented as a class for some other reason.
Hence, whether or not the value can be represented as a constant is an orthogonal problem. Clearly, let r = Rect(...) creates a constant r that does not even require to be stored in an abstract location, yet r does not have value semantics.
It is convenient that Swift can rule out this case by implementing Vec2 as a struct rather than a class (because Int also has value semantics), and probably the reason why the inductive definition offered by @algal seems so appealing. Unfortunately, this approach does not cover all cases, as mentioned by @dabrahams.
I would grant, however, that the inductive definition does work if base cases are defined asana types having value semantics, rather than attempting to identify a specific set of "primitive" types.
I do not think it would, as long as one can guarantee that uniqueness of mutable access is preserved, and this can be achieved in different ways. In Swift, we use inout, in Rust, we use borrowing. In both case, the mechanisms preserve value independence.
I think we should separate the definition of "value semantics" from the language features that can be used to implement it.
I also think that the mere ability of a language to break the invariants of a given abstraction does not mean the abstraction is moot. One can break value independence in Swift, yet we happily claim that Int has value semantics.
var i = 99
func f(x: inout Int) { x = i + 1 }
f(x: &i) // Oops ;)
Unfortunately, keywords and annotations are typically insufficient to describe value semantics. At the very least, they are in Swift. Here are two examples:
var i = 99
struct Foo {
var p: Int { i }
}
struct Bar {
private var p: NSArray
}
Foo does not have value semantics, because variables of type Foo can be changed externally. Yet this type definition does not feature the class keyword.
Bar may or may not have value semantics, depending on the intended meaning of the relationship with the value of the property p (independently of the ability to break its intended semantics). Note that private is insufficient to prescribes that Bar has value semantics, because p's value might be aliased when Bar is instantiated.
Incidentally, I think these observations partially explain why defining a ValueSemantic protocol is rather challenging.
They are part of Rect. The variables/properties are part of the Rect structure, if these all belong to the same resource is another question, I would say probably not, because they must be able to alias different resources.
Maybe in Swift, but generally this is also false. For var p: Int, it is possible that another variable exists pointing to the same resource representing an Int, in Swift this isn't possible because of the copyable behavior, this behavior is intentional but not a specific property of integer variables.
I do not think it would, as long as one can guarantee that uniqueness of mutable access is preserved, and this can be achieved in different ways
How is this guaranteed?
I think over keywords which relate to variables (identifiers) and not directly to types.
I think we should separate the definition of "value semantics" from the language features that can be used to implement it.
Then an Int hasn't Value Semantics because I could higher order alias to an integer (i.e. Ref<Int>) which isn't possible in Swift, but if we disregard language features?
Note, we would probably don't introduce Ref<Int>, instead simply use Int because the reference/resource model shouldn't be confused with the type system as it is the case for Low Level languages, they are both different parts of a language yet influencing each other.
I also think that the mere ability of a language to break the invariants of a given abstraction does not mean the abstraction is moot.
Only if this is an unsafe operation, otherwise the abstraction is moot or must be re-adapted to a new definition.
If this really changes i, then this is wrong. I would not argue so as inout should make a copy here and rebind the current value to x and not to i.
This is not true and is sadly feature dependent. If we had generally inout variables as locals, then yes, we could theoretically assert value semantics for this inout annotated variable independent to the underlying type.
The same would be true for pure annotated variables.
Personally, I find the concept of value types confusing and unnecessary. A type shouldn't require to be Value Type nor to provide value semantics.
Therefore, I would prefer to use only one of these entities: Struct or Class and not both as it is the case for example in Julia. Using classes as structs could be made possible with a final tag and insisting on explicit copy behavior can be configured with a modifier like copy or copy(cow) though the compiler could theoretically decide that itself.
But being said that, I think it is too late to talk about these things.
It seems to me that you are using the term "resource" the same way I am thinking of the value(s) of a type. Interestingly, the question remain: are these properties part of the resource? "probably" is not as satisfying as "definitely".
Hence, a definition of "value semantics" that is able to answer this question more clearly is appealing.
You are right.
I was implicitly talking about Int from Swift's standard library.
Formally, you establish the guarantee by showing the soundness of your mechanism with respect to a definition uniqueness. Less formally, you simply ask your users to trust that the rules of your compiler are sound with some catchy slogan, like "fearless concurrency" ;)
Once again, I'm afraid I didn't fully understand your point.
You can create Ref<Int> in Swift, with reference semantics, and yet I still hold that Int has value semantics in Swift.
class Ref<T> {
init(_ value: T) { self.value = value }
var value: T
}
I respectfully disagree, and still do not think an abstraction only makes sense if it is enforceable by the language. For instance, it makes sense to define termination, even if termination is undecidable in general, because I can use this definition to reason about my code.
If I stumble upon an API documentation that says "this function always terminates", then I don't need Swift compiler to prove it for me for the remark to be useful. The same goes for any semantic property that you can think.
Unfortunately, the distinction between struct and class is not sufficient to capture the definition of value semantics I use, as I have demonstrated with my examples. A java-like final keyword (which is essentially Swift's let) also falls short, because it only affects the reassign ability of the variable, without any impact on the referred value.
I would rather ask if those properties/variables point to a subResource of the resource pointed by the instance struct variable rect:Rect. It depends on the context for the whole program lifetime and which resource-model the compiler whould choose for these properties in this context, but generally a var is allowed pointing to any resource.
So in average of contexts, no they aren't a part of the resource for the surrounding struct instance.
Hmm, I was referring to the approach Swift takes into consideration, it prefers the control over keywords more than over types. Fur Rust, it seems uniqueness is controlled more over types.
So yes, you are right, there are different ways to solve.
Yes, but you simulate the resource model given the concepts in the language, you can't really get an Ref<Int> value of an existing integer maybe with some kind of unsafe operation, I don't know.
But even then, changing an integer by reference would change all other vars pointing to the same resource represented by the reference undermining value semantics for integers.
I think we are talking past each other. Yes termination is a useful concept, but is it enforced by functions or control structures? No.
Functions/procedures and control structures are naturally partial but can be total.
so with treating Int as a type with value semantics, I wouldn't even expect that behavior, instead I would assume the auto-generated closure takes a copy of a, but it seems it does not.
Well, I think one could still argue that a is "value semantic", if we consider b.value is equivalent to call { a }() at the call site, and b.value += 2 is equivalent to { a += 2 }(), i.e. the value of a is always captured, copied, assigned "on-site".
--edit: additional thoughts--
From the memory point-of-view, (despite the actual compiler implementation and optimizations),
here, Int followed "value-semantic" because the instance a is mutated, and once a struct is mutated, we could mentally picture they are copied and being two different objects, and they may be allocated with different memory address. By contrast, if Int is "reference-semantic", we would mentally assert instance a still lives in the same address.
I think in the above example, b: MutableReference<Int>.value += 2 is more like calling a function func set(_ a: Int) -> Void.
The debate is probably, whether we consider the b is "value-semantic", and I personally would treat it as not, despite its a struct.