[swift-users] Are value semantics really appropriate in a diagramming app?

Dave Abrahams dabrahams at apple.com
Tue Aug 2 15:07:39 CDT 2016


on Mon Aug 01 2016, Rick Mann <swift-users-AT-swift.org> wrote:

>> On Aug 1, 2016, at 19:18 , Jack Lawrence <jackl at apple.com> wrote:
>> 
>> Jens: Why? There are significant benefits to value semantics for
>> this type of problem, for the reasons laid out in the WWDC
>> videos. It would be helpful to know why you disagree in this
>> case—maybe there are solutions to the issues you’re thinking of.
>> 
>> Rick: I’d think that value semantics would be the right choice
>> here. When you do a mutation, you would copy the state of the entire
>> diagram. It should be efficient via COW, but if not you can
>> implement you own more fine-grained COW types with
>> isUniquelyReferenced(). This would allow you to easily support
>> things like undo.
>
> The more I consider this, the more I think value semantics won't work
> for me. I think, to take advantage of the easy undo feature, my entire
> model *must* be implemented with value semantics.

That certainly helps.  You could introduce an explicit copy operation
to get around the use of classes, but that can get very messy.

> But my model has implicit reference semantics: multiple instances of a
> part can share a PartDefinition; it is intended that if the
> PartDefinition changes, all the referencing instances get the
> change. 

That is definitely a reference.  However, there are lots of ways to
represent references such that the entire model still has value
semantics.  Reference semantics, in the broadest sense, are everywhere:
as soon as you have an array and an integer, you have reference
semantics.  The problem with using classes is that they introduce
reference semantics *implicitly* and *prolifically*.

So, for example, if you are implementing a model and you want to
represent a selection as a separate data structure, then you'll need to
give every selectable element some kind of id, so you can store the ids
there. One way to do that is to store all your elements in an array in
your model, and use the index into the array as the id.  Then when one
element needs to refer to another element, it stores the ID of that
element.

[Aside: one of the simplest and most efficient representations of a
generalized graph structure is `[[Int]]`, which has value semantics.
Each element of the outer array corresponds to a vertex, and each
element an inner array represents the target of that vertex's outgoing
edges]

The most obvious thing you don't get from this kind of arrangement is
automatic lifetime management: an element doesn't disappear just because
you've stopped referring to it.  Whether that's appropriate for your
application or not is a question for you to answer.  

You can selectively recreate as much of the implicit behavior of classes
as you like, e.g. storing reference counts to recreate automatic
lifetime management and/or threading a free list through the array to
maintain ID stability, but of course at some point it becomes silly.

Where your particular application falls in this spectrum is for you to
say.  The fact that there's a component of reference semantics in your
model doesn't mean you can't use value types, and the fact that using
value types has some awesome benefits doesn't mean you can't use
classes.  Weigh the tradeoffs and make an informed choice.

> There are additional situations in which reference semantics are at
> play, as well: a PartDefinition can have one or more labels, but each
> instance can specify the relative location of the label for that
> instance. So, there is struct that contains a position and a reference
> to the label in the PartDefinition. But if the contents of the label
> changes, all the instances need to see that change.
>
> I don't think I get to take advantage of value semantics, and it makes
> me wonder if any typical, non-trivial model's object graph really has
> no reference semantics.

Some do, but many-to-one relationships are an important concept, and
many applications need to represent them somehow.  I've personally found
that most such relationships are best expressed explicitly, which allows
me to preserve value semantics of the whole system. YMMV, of course.

HTH,

-- 
-Dave



More information about the swift-users mailing list