> On May 7, 2016, at 11:51 PM, Dave Abrahams <dabrah...@apple.com> wrote:
> 
>> Does Array<T> have value semantics then only if T also has value
>> semantics?
> 
> This is a great question; I had to rewrite my response four times.
> 
> In my world, an Array<T> always has value semantics if you respect the
> boundaries of element values as defined by ==.  That means that if T is
> a mutable reference type, you're not looking through references, because
> == is equivalent to ===.
> 
> Therefore, for almost any interesting SomeConstraint that doesn't refine
> ValueSemantics, then
> 
>  Array<T: SomeConstraint>
> 
> only has value semantics if T has value semantics, since SomeConstraint
> presumably uses aspects of T other than reference identity.  

I just had a chance to digest Dave's answer. It explains a lot.

PureValue was defined in terms of the type's physical representation:
- A struct with no reference properties
- Recursively, a reference to immutable or uniquely referenced memory.

It's defined such that we can say Array<T> is a PureValue iff T is a PureValue.

There is currently no procedure for determining PureValue because we have no 
way to declare that references are immutable or uniquely referenced. It would 
be a promise by the developer.

Now attempting to look at it from Dave's direction, value semantics apply to 
the variable's type, not the object's physical representation:

let v2 = v1
f(v1)
assert(v1 == v2)

If everything is a value, then this always works. Great!

If the variable's type does not allow mutating shared state, then operations on 
the variable are operating on a value.

protocol ValueP {
  func compute() -> Result // nonmutating
}

func g(v1 : ValueP) {
  let v2 = v1
  v1.compute()
  assert(v1 == v2)
}

Nice. ‘compute' cannot change the value. Those value semantics do not tell me 
anything about shared state or function purity. For that, I need some 
additional constraint on 'compute'. Knowing that it does not mutate the 'self' 
value is insufficient.

One way of doing that, for example, is to declare that 'compute' transitively 
cannot access globals *and* ValueP must be a PureValue. Now I can safely write 
this:

protocol ValueP : PureValue {
  @strawman_noglobal func compute() -> Result
}

/// Return (v1.compute, v2.compute)
func g(v1 : ValueP, v2 : ValueP) -> (Result, Result) {
  let r1 = v1.compute()
  if v1 == v2 {
    return (r1, r1)
  }
  return (r1, v2.compute())
}

So, Dave is right that we need to decide soon whether we can make stronger 
assumptions about value semantics. But that is a separate question from how to 
express function purity. I don't think there is any urgency in introducing 
things like the PureValue protocol or @strawman_noglobals attribute, now that 
we have clearly established shared-state-mutability-by-default. When we want to 
seriously have that discussion, we should consider other alternatives. I would 
prefer to wait until indirect structs and improved CoW support have had more 
discussion.

-Andy
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to