[swift-evolution] Should we rename "class" when referring to protocol conformance?

Matthew Johnson matthew at anandabits.com
Mon May 9 18:06:59 CDT 2016


> On May 8, 2016, at 1:02 AM, Dave Abrahams <dabrahams at apple.com> wrote:
> 
> 
> on Sat May 07 2016, Matthew Johnson <matthew-AT-anandabits.com <http://matthew-at-anandabits.com/>> wrote:
> 
>>> 
>>> You haven't answered this question.  How would you use this protocol?
>> 
>> I think the best example was given by Andy when discussing pure
>> functions.  Maybe I want to write a generic function and ensure it is
>> pure.  I can only do this if I know that any arguments received that
>> compare equal will always present the same observable state.  
> 
> And that it doesn't touch any globals.
> 
>> For example, maybe I wish to memoize the result.
>> 
>> I cannot write such a function for all T, and I also cannot write such
>> a function for all T that have value semantics if we adopt the
>> “references are values” view of the world.  
> 
> Oh, you absolutely can, because if the function applies to all T that
> have value semantics, it only has a few operations to work with:
> initialization, assignment, and equality.  Assignment is the only
> mutating one of these.

I was implicitly assuming additional constraints exposing other behavior.  I should have stated that explicitly.

> 
>> I need an additional constraint that rejects things like
>> Array<UIView>.  (T would obviously also be constrained by a protocol
>> that exposes the properties or methods my function requires to compute
>> its result)
> 
> Did you just start referring to T as the element type of the array
> instead of the function's parameter type?  I think you're
> unintentionally pulling a fast one, reasoning-wise.  It might help to
> write down some actual code.

I was intending to refer to T as the element type of the array all along.  The signature I was thinking of would look something like:

pure foo<T>(bar: [T]) -> SomeReturnType

I should have written this down to be clear about it.

> 
>> In general, it would be used where you need to ensure that the result
>> of any operation observing the state of any part of the aggregate
>> value will always return the same value at any point in the future.
>> If I observe a[0].foo now I know with certainty the result of
>> observing a[0].foo at any point in the future.  
> 
> Sure, but what you need then is a constraint on a's Element type that it
> has value semantics, not some kind of new PureValue concept to use as a
> constraint on the array itself.  

That works if you follow all observable paths until you get to scalars.  For example maybe A’s element is Array<Array<UIView>> and `foo ` is implemented in an extension off Array where Element == UIVIew (after we get same type constraints).  Once we have conditional conformance that extension could conform to a protocol that makes `foo` visible and it would still have value semantics but it would not be a pure value.

> 
>> This aspect of preservation of observed values across time is
>> essential to the distinction between Array<LayoutValue> (see below)
>> and Array<UIView>.  It doesn’t matter when I observe the frames of the
>> elements of Array<LayoutValue>, I will always get the same rects back.
>> With Array<UIView> that is obviously not the case as the frame of the
>> view could be mutated by anyone with a reference to the views at any
>> time in between my observations of the frame values.
>> 
>> struct LayoutValue {
>> 	frame: CGRect
>> }
>> 
>>> 
>>>>           let t = MyClass()
>>>>           foo.acceptWrapped(Wrap(t))
>>>>           t.mutate()
>>>> 
>>>>           In this example, foo had better not depend on the wrapped instance
>>>>           not
>>>>           getting
>>>>           mutated.
>>>> 
>>>>           foo has no way to get at the wrapped instance, so it can't depend on
>>>>           anything about it.
>>>> 
>>>>           Ok, but this is a toy example. What is the purpose of Wrap? Maybe
>>>>           foo
>>>>           passes the
>>>>           wrapped instance back to code that *does* have visibility to the
>>>>           instance. My
>>>>           point was that shared mutable state is still possible here. 
>>>> 
>>>>           And my point is that Wrap<T> encapsulates a T (almost—I should have
>>>>           let
>>>>           it construct the T in its init rather than accepting a T parameter)
>>>>           and
>>>>           the fact that it's *possible* to code something with the structure
>>>>           of
>>>>           Wrap so that it has shared mutable state is irrelevant.
>>>> 
>>>>           The point I am trying to make is that the semantic properties of
>>>>           Wrap<T> depend
>>>>           on the semantic properties of T (whether or not non-local mutation
>>>>           may be
>>>>           observed in this case). 
>>>> 
>>>>       No they do not; Wrap<T> was specifically designed *not* to depend on the
>>>>       semantic properties of T. This was in answer to what you said:
>>>> 
>>>>               A struct wrapping a mutable reference type certainly doesn’t
>>>>           “feel” value semantic to me and certainly doesn’t have the
>>>>           guarantees usually associated with value semantics (won’t
>>>>           mutate behind your back, thread safe, etc).
>>>> 
>>>>       I have been trying to get you to nail down what you mean by PureValue,
>>>>       and I was trying to illustrate that merely being “a struct wrapping a
>>>>       mutable reference type” is not enough to disqualify anything from being
>>>>       in the category you're trying to describe. What are the properties of
>>>>       types in that category, and what generic code would depend on those
>>>>       properties?
>>> 
>>> Again, the key questions are above, asked a different way.
>>> 
>>> -- 
>>> -Dave
> 
> -- 
> -Dave

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160509/a57108bf/attachment.html>


More information about the swift-evolution mailing list