Keyword for immutable instances

IME, you tend to notice it pretty quickly when you fine tune the memory usage (though static analysis would always be welcomed there). That or it is on so small scale that it doesn't affect the performance, which is also good. Luckily, for most cases, you can easily get rid of it once you notice it.

One more thing here, this is just an implementation details. Nothing prevents you from creating a ChunkedArray that chunks its own storage every 100 element, so that when the mutation occurs, only the chunks that mutate need to be copied. Probably because there are nuances like this that CoW is still relatively a manual work.

1 Like

Protocols only have access to properties that are declared in the protocol, and they only have write access if the protocol says that the property has to have a setter. I'm not sure what problems you've seen, but what you've described is not normal.

Protocols exist to enable polymorphic code. For instance, this function will work on Array, Data, UnsafeMutableBuffer, or some custom collection. And it will always be correct if C implements MutableCollection properly.

func updateEach<C: MutableCollection>(in collection: inout C, transform: (inout C.Element) -> Void) {
    let end = collection.endIndex
    var i = collection.startIndex
    while i < end {
        transform(&collection[i])
        collection.formIndex(after: &i)
    }
}

PS. This really should be a method in an extension, but I thought a generic function might be easier to explain.

That's what I said. It was Objective C.

That is not true. The compiler ensures that a let instance is not directly mutated, but there's no syntax for checking if a particular instance of a struct was declared as let or var.

1 Like

An additional point here is that this is not the first time you've lumped in Int with String (and Array). This implies a fundamental misunderstanding of Swift's structs and the facilities for CoW.

1 Like

Again, this is exactly the use-case that Array's implementation covers. It uses CoW to maintain a single copy of internal storage until a piece of code attempts to change the array's contents. You can freely pass (copies of) the array around without any performance cost or memory cost. You also get the benefits of a let declaration, for which the compiler will enforce immutability. Only copies of that array could be mutable.

I mentioned this before, and you did not explain how it would be insufficient to cover any particular use case.

2 Likes

Objective-C protocols are very different from Swift protocols. In Objective-C protocols are duck typed, very dynamic, and do nothing to prevent you from calling methods that aren't part of the protocol.

When Xiaodi meantioned Protocol Oriented Programming, he was referring to the much stricter Swift protocols. Those only allow generic code to use functionality from the protocols their types conform to. They certainly aren't reaching into ivars willy-nilly.

1 Like

It is a truth universally acknowledged, that a single man in possession of a good fortune, can write spaghetti code in any language.

7 Likes

const doesn't mean that the object is guaranteed not to change. The point of it is to tell you - or for you to tell the compiler - that you are not allowed to change the object in that context. It expresses the intent of how the object will be used in that scope. So it's this ability to better express my intent to the compiler that I find valuable.

Surely const is better than nothing. What I don't understand though, is why we're having this conversation when Swift provides a stronger (let on struct) contract that "you, and nobody else are allowed to change the object", which I think is much easier and more valuable.

2 Likes

It's stronger, but it's also potentially more confusing for the newcomer, because the (im)mutability appears to be part of the declaration, not the type itself. NSArray vs NSImmutableArray is really clear, but...

let x = [1, 2, 3, 4]
var y = x

x.append(5) // compiler error
y.append(6) // just fine

...might lead someone to think that assigning x to var y suddenly means that x's immutability can be subverted via y.

1 Like

We're having this conversation because of the OP's point that we don't have a way to say that a class instance should be considered immutable, as we do with structs. All we can do with let is say that the reference can't be changed to point to a different object.

Sure, but I don't think the way C++ handles const, including the non-const -> const casting, is anywhere near Swifty enough to garner attention. And fixing that handling has just been wrapping us back into Swift struct.

I couldn't resonate with that. C++ already has the same scenario for at least basic data types and basic structs.

const auto x = 3;
auto y = x;

x++; // compiler error
y++; // just fine

And programmers seem completely comfortable by the fact x and y are distinct entities. Now, we're just pushing it onto larger, more complex types like standard collections.

Except if you propose to remove all Unsafe API from Swift, it will always be possible to change any address in memory, including any address used by fixed objects.

And even if you remove unsafe API, as long as Swift links on System library and include all libc functions into its memory, you can do anything (including removing a read-only flag on a page of memory).

For sure, in the case of ints, I don't think anyone would intuitively think that adding 3 to 4 would somehow make 4 turn into 7, my issue is exactly with more complex datatypes.

Most developers know the difference between what a value and a reference type represent, but when it comes to the semantics, I think there'd be plenty of otherwise decent devs who would only have a surface understanding at best, and would find the behaviour of the code I posted surprising.

This is compounded by the fact that their is no difference in syntax between working with value types versus references, and that when working with the difference between a mutable and immutable value type, the mutability appears to be a property of the declaration, not the the type itself, in that if you have a mutable value type, there is no way for a method to return an immutable version.

FWIW I'm not saying there is anything wrong with Swift's handling of value or reference types at all, just that there are areas for confusion.

Actually, it's the opposite. Return values are always immutable.

That said,

func f() -> Int { return 3 }

var y = f()
y = 4

still works because the immutable value from f() is written into a location that is declared to be mutable. It's no different than

var y = 3
y = 4
1 Like

Sorry, yes, I should have been more clear - what I meant was that the interface of the value type you get back still has mutating methods on it, so someone who is less experienced with value semantics might think that the value they get back is mutable in the same 'sharing' way as a reference type.

It seems there are really 3 types of data in Swift:

  • values
  • CoW values
  • references

I would suggest that each should have its own syntax so as not to confuse people who are new to the language. e.g.

  • var foo : Int = 5 ; foo := foo + 3
  • var bar = [1,2,3] ; bar.append(5) // only alterable with methods
  • var bat = Thing(1) ; bat = Thing(2)

CoW is an optimization. If its usage by a value type causes confusion for the developer, the type is poorly designed and/or implemented.

From the perspective of the user of a value type, there's only one rule to remember: effectively, value types are always copied. Any implementation that breaks this illusion is broken.

5 Likes