

On Swift’s array semantics - joeyespo
http://yinwang0.wordpress.com/2014/07/13/swift-array/

======
kipple
According to the Swift language guide Strings, Arrays, and Dictionaries are
all copied on assignment to a variable. So wouldn't the author's primary
assumption (that let a and var b refer to the same array) be false? And this
whole issue is null?

[https://developer.apple.com/library/prerelease/mac/documenta...](https://developer.apple.com/library/prerelease/mac/documentation/Swift/Conceptual/Swift_Programming_Language/ClassesAndStructures.html)

 _Assignment and Copy Behavior for Strings, Arrays, and Dictionaries

Swift’s String, Array, and Dictionary types are implemented as structures.
This means that strings, arrays, and dictionaries are copied when they are
assigned to a new constant or variable, or when they are passed to a function
or method.

This behavior is different from NSString, NSArray, and NSDictionary in
Foundation, which are implemented as classes, not structures. NSString,
NSArray, and NSDictionary instances are always assigned and passed around as a
reference to an existing instance, rather than as a copy.

NOTE The description above refers to the “copying” of strings, arrays, and
dictionaries. The behavior you see in your code will always be as if a copy
took place. However, Swift only performs an actual copy behind the scenes when
it is absolutely necessary to do so. Swift manages all value copying to ensure
optimal performance, and you should not avoid assignment to try to preempt
this optimization._

~~~
goblin89
> According to the Swift language guide Strings, Arrays, and Dictionaries are
> all copied on assignment to a variable.

This might've been a recent change in docs. Quoting part of the same section
from my copy of the Swift book downloaded a while ago (emphasis mine):

‘The assignment and copy behavior for Swift’s Array type is more complex than
for its Dictionary type…

‘If you assign an Array instance to a constant or variable, or pass an Array
instance as an argument to a function or method call, the contents of the
array _are not copied_ at the point that the assignment or call takes place.
Instead, both arrays share the same sequence of element values. When you
modify an element value through one array, the result is observable through
the other.

‘For arrays, copying _only_ takes place when you perform an action that has
the potential to modify the length of the array.’

As an aside, it would be nice if Apple offered some kind of change log or a
“what's changed” page for Swift docs (though to my knowledge they had made no
promises regarding spec stability yet).

~~~
josephlord
Revision history for the main Swift book:

[https://developer.apple.com/library/prerelease/ios/documenta...](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/RevisionHistory.html)

Note that you have to delete the book in iBooks and download it again to get
the new version.

------
blahedo
I wish I could upvote this a hundred times; rarely have I seen so well-laid
out a post on such a subtle topic. Just as I would form a thought like, "the
author needs to explicitly break this down as an issue of variables vs
values", they did so.

Part of the difficulty here (and part of why others are having a hard time
understanding it) is that immutable arrays and mutable arrays are really two
different types, and unlike many other types (which are inherently one or the
other), you really want both. A Java analogy is that there are two different
string types: String, which is immutable, and StringBuilder, which is
mutable[0]; but this is a property of the value, not of the variable. (Other
languages have similar string classes, and Swift might well also.) For C++
folks, the distinction between mutability of variable and mutability of value
is shown in the placement of "const", i.e. "const foo ∗" vs "foo const ∗"
(which of course leads us to "const foo const ∗", and the whole thing is a
syntactic mess, but anyway), and object values which are mutable can behave
differently from values which aren't even when the underlying type is the
same. In your own code in any language, of course, you can design a class so
that its objects are immutable simply by not giving the user any access to set
instance variables; and if you wanted both mutable and immutable versions,
even without particular language support, you could make a MutableFoo and an
ImmutableFoo that are copy-constructible from each other.

What may confuse the issue here is the idea that we might want brief syntax
for both kinds of array. But whatever the language designers decide, the OP is
exactly right to point out that they really, really need to think this through
and divorce mutability of values from mutability of variables.

[0] Technically three, because StringBuffer is also there, but that's not
relevant to this discussion.

~~~
cscurmudgeon
This is an excellent post.

A meta comment (not on Swift): the number of new languages with C-like syntax
and nothing revolutionary semantically is baffling. Do we really need to
master yet another C-like syntax to do X where a library (or an existing
language) would have sufficed? Is this the new way to create technological
walls?

~~~
nardi
Are you kidding? Swift has a TON of revolutionary stuff over C, including
algebraic data types, optionals, destructuring pattern matching, and a lot
more. Rust too. There are a ton of new "C-like" languages that are light years
beyond C99.

Edit: Not to mention automated memory management!

~~~
cscurmudgeon
I meant C-like syntax. The laundry list of features you mentioned is present
in lots of existing mature languages (not C).

I don't understand the need to create something rickety without significant
innovation (over any relevant language (e.g. SML, Haskell, Racket) and not
just C).

~~~
nardi
You complained about new C-like languages (C-like in syntax) not being any
better than C. I mentioned some ways that new C-like languages (C-like in
syntax) are much better than C. Now you're complaining that other languages
already had those features. I don't contest that. Doesn't change the fact that
your first comment was misguided.

~~~
cscurmudgeon
You misunderstood my first comment. I meant new C-like-in-syntax have nothing
revolutionary semantically _across the board._ This is a double blow as each
new C-like syntax is difficult to master (compared to Lispy languages) and
contributes nothing semantically new.

~~~
nardi
> You misunderstood my first comment. I meant new C-like-in-syntax have
> nothing revolutionary semantically across the board.

Are you saying that the new features in Swift/Rust/C#/etc. aren't
revolutionary? Or that they're not "across the board"? Or that they're not
semantic? Which part do you disagree with?

> This is a double blow as each new C-like syntax ... contributes nothing
> semantically new.

This is demonstrably false. Do you mean that you don't think the new semantics
are revolutionary enough? Or "across the board" enough?

Either way I have to disagree with you. Automated memory management is a
semantic game changer, and applies nearly everywhere. Same goes for type
inference, and optionals. ADTs are huge too. If your argument is that these
are not important semantic improvements over C, then IMO you could not be more
wrong.

~~~
cscurmudgeon
Why do you seem so bent on misunderstanding me? I am not talking about
semantic improvements over just C!

E.g,

> Automated memory management is a semantic game changer

That has been there in other languages for decades.

By "across the board," I meant across all languages not just C-like languages.

~~~
nardi
Ah! That wasn't clear to me from your wording. I think I was especially
confused because that means your argument seemingly boils down to "only the
first language to have a feature is notable." (And I guess also that C-like
languages are hard to learn?) But all languages stand on the soldiers of
giants. I don't look down on Haskell simply because other languages had its
features first.

~~~
cscurmudgeon
Agreed. I am not looking down on Swift. It seems like all the human capital
could go into making existing languages more awesome.

------
jakejake
I'd probably prefer that var b = a would just generate an error. You would
have to explicitly copy or clone an immutable object in order to create the
variable.

~~~
tl
I'm glad I'm not the only one thinking this. What's the point of Swift being
"safer" if this isn't an error (or at least a warning). The standard library
even includes -[mutableCopy], so it's a established convention.

~~~
egwor
agreed; this would be a nice exception and I'm sure in a few places the
compiler could pick this up to add warnings. Alternatively get it to throw an
exception if the array is modified. Much better than some [[]] notation which
just makes me think of multidimensional arrays

------
tel
I don't see anything wrong with the beta3 semantics. I see it as problematic
that (=) is so casually overloaded between "assign" and "equivalate".

> Now aliasing an array will cause the array to be copied. Isn’t that weird?

But assignment to a mutable reference is just that, assignment not
equivalation. It is not merely aliasing. We do not desire the ability to
freely replace "b" with "a" so why assume it holds?

Sure this might vary from programmer's mental models, but that is the fault of
the programmer after a point. I expect all hammers to work roughly the same,
yes, but I expect a laser cutter to work under very different principles. I
must learn them.

------
josephlord
I disagree with this post. The semantics are clear and simple: if you
assign,maps to a function or return from a function any array you can treat it
as a copy of the array. As a performance optimisation the copy usually won't
happen as it won't be needed. If you are dealing with large enough arrays that
it matters wrap the array in an object that can be passed by reference.

I strongly opposed to original semantics and blogged about it. I am happy with
the replacement.

~~~
josephlord
Much too late to edit. I should have said "...assign, pass to a function..."

------
andymoe
Then what is the point of having let vs var at all? I think the solution they
came up with is the correct one given the design of swift and its use of let
and var keywords.

~~~
masklinn
let v var has to do with mutable bindings, value mutability within the
bindings should be a separate concern.

Rust makes that exact difference[-1]: `mut` can be used either at the binding
level

    
    
        let mut a: &int;
    

or at the type level:

    
    
        let b: &mut int = &mut 1;
    

Both are references (~pointers) to an int, but `a` is a mutable reference, it
can be changed to point to a new value[0]:

    
    
        a = &2;
    

because b is an immutable reference it can't point to a new value and

    
    
        b = &mut 2;
    

is a compilation error:

    
    
         error: re-assignment of immutable variable `b`
    

_however_ b's _value_ can be modified in place:

    
    
        *b += 1;
    

whereas trying that with a:

    
    
        *a += 1;
    

is an error:

    
    
        test.rs:5:5: 5:7 error: cannot assign to immutable dereference of `&`-pointer `*a`
    

`a` is a mutable reference to an immutable value, `b` is an immutable
reference to a mutable value.

[-1] and so does C though it has the opposite defaults, you can have an

    
    
        int const *
    

— a mutable pointer to an immutable int (you can reassign the pointer but not
change the pointee) — and an

    
    
        int * const
    

— an immutable pointer to a mutable int (you can't reassign the pointer but
you can alter the pointee)

[0] the code does not work as-is because lifetimes, but close enough

~~~
alexatkeplar
I didn't appreciate that Rust makes that distinction, that really is quite
elegant.

By way of comparison (to Rust and the original article), here is Scala:

    
    
        scala> val a = scala.collection.immutable.Set("apple", "orange")
        a: scala.collection.immutable.Set[String] = Set(apple, orange)
    
        scala> a = Set()
        <console>:8: error: reassignment to val
               a = Set()
                 ^
    
        scala> val b = scala.collection.mutable.Set("cherry", "plum")
        b: scala.collection.mutable.Set[String] = Set(cherry, plum)
    
        scala> var c = b
        c: scala.collection.mutable.Set[String] = Set(cherry, plum)
    
        scala> b += "pear"
        res6: b.type = Set(pear, cherry, plum)
    
        scala> c
        res7: scala.collection.mutable.Set[String] = Set(pear, cherry, plum)
    
        scala> c = a
        <console>:10: error: type mismatch;
         found   : scala.collection.immutable.Set[String]
         required: scala.collection.mutable.Set[String]
               c = a
                   ^
    

In other words: var versus val has to do with mutable bindings; mutable
bindings allow you to mutate the value but not the type; object mutability
within the bindings is governed by the object's own type (as the original
author recommended for Swift).

~~~
keeperofdakeys
The different types of mutability are also quite important for the concept of
"borrowing" in Rust, which is simply taking a reference. The compiler ensures
that ownership of the variable isn't transferred while any such reference is
active, and only one mutable reference (of a mutable type) can exist at any
one time. This means that bugs like that shown for Swift are impossible in
Rust, because the language ensures no two things can ever modify it at the
same time.

------
ddp
I rather like the copy-on-write semantics of Beta3. And if the OP wants the
Swift team to consider his/her comments, they should file a Radar like
everyone else.

------
clscott
The syntax proposed has problems. Many languages allow you to declare nested
data structures and a natural way to express an array of arrays in swift would
be the following:

    
    
      let a = [[1,2,3],[6,7,8]]

~~~
nemetroid
Can you give an example for which the proposed syntax would be problematic?
Your example would be written as:

    
    
        let a = [: [: 1, 2, 3 :], [: 6, 7, 8 :] :]
    

or

    
    
        let a = [ [: 1, 2, 3 :], [: 6, 7, 8 :] ]
    

etc., depending on what mutability you want.

------
mostly_harmless
This is not a good solution. This adds complexity (syntactically and
conceptually) without adding any feature. The immutability is already implied
by the 'let' and 'var'.

Additionally, specifying that the RValue "[1,2]" has mutability is
meaningless. The expression "[1,2]" is represented as a constant until it is
stored in a variable (it should be represented in the MACH-O constant data
section). It is the variable which has the concept of mutability.

The proposal is similar to proposing the following declaration in C:

"int i = const 1;"

Which is meaningless, because the '1' can never be not-const

~~~
masklinn
> The immutability is already implied by the 'let' and 'var'.

The immutability of the binding and that of the value are different concerns.
let and var are about the binding's mutability, trying to munge them together
only ends up in tears.

> The proposal is similar to proposing the following declaration in C:

No. The proposal is similar to the following C:

    
    
        int const * i;

------
ryanpetrich
The proposed syntax creates ambiguity between immutable arrays and mutable
arrays containing only a single mutable array.

~~~
skrebbel
A solvable ambiguity (nested C++ template parameters, anyone?) and besides, it
was just an example. I don't think the author really meant to "propose new
syntax", but rather say "this is a real problem that needs solving and I'm
sure the Swift team can come up with syntax for it if they want it to be
succinct".

------
twic
_A similar but more subtle problem happened to Java’s arrays regarding
aliasing and subtyping a long time ago, which could crash the JVM._

Really? I don't remember that, and i was writing Java a long time ago. I'd be
interested to be reminded about this bug.

------
binarycrusader
The actual article title is currently: "Design mistakes in Swift language’s
array"

~~~
dang
We didn't change it. I assume the rewording was joeyespo's attempt to avoid
misleading or linkbait content in the title. As the HN guidelines point out,
that's fine.

