AnyValue, Immutable protocols, deprecate ": class" protocol constraint

There actually might be a subtle difference between : class and : AnyObject as @codafi pointed out in this stackoverflow answer: swift - What's the difference between a protocol extended from AnyObject and a class-only protocol? - Stack Overflow
Not sure if this is still valid or if this has changed in the meantime.

Other than that, should a protocol really dictate the semantics of a type that implements it?

To me it seems at the moment that limiting an implementation to be a class is a workaround for an ownership problem. Other than having to store a reference to an implementing type without taking a strong reference of it (weak) I wouldn't know when I ever want to restrict a protocol to be class-only.

For what purpose would you need : AnyValue?

: class is already deprecated as per SE-0156 but @Slava_Pestov couldn't implement it in time or something like this.

There is no real benefit form : Struct or : Enum constraints. The naming behind : AnyObject also implies that in the future there might be more reference types which can conform to protocols and not just the class as we know it.

I raised my hand for the : AnyValue constraint before. Please everyone who's going to jump into this discussion don't mess it up and make another huge thread about value semantics because the AnyValue has nothing to do with value semantics as it's a totally different problem to this constraint.

I’ll hold off on the comments about value semantics for now. Can you remind us what use cases you know of for this constraint that do not implicitly imply value semantics? I don’t recall anyone ever producing a compelling answer to that question.

1 Like

I don't think there's a real use case... but if there was a way to enforce "structness" ;-), some useful things might be possible:
Value types which are guaranteed not to contain any class properties would not only be "truly" immutable, but could also be serialized and restored without detailed knowledge about the internal structure.

I see the subtype relationship a little different:

  • SomeClass : (May or may not conform to ValueSemantical) : AnyObject : Any
  • SomeValue : (May or may not conform to ValueSemantical) : AnyValue : Any

Classes can have value semantics as well and if possible I'd have an explicit constraint for that too so that we can guarantee it. Swift's value types are designed around value semantics but there is no guarantee that they always behave that way, at least no explicit one. You may not always want your structs to have value semantics, but that depends on what you're designing.

1 Like

This doesn’t even begin to answer the question of how you think AnyValue would be used. What use cases are there? What generic code could be written with it that cannot be written today?

Well I had a use case for that this week which hit me right in the face during an app review. This operator caused the issue because it was not constrained to value types:

public func <- <T>(lhs: T, rhs: (inout T) throws -> Void) rethrows -> T {
  var value = lhs
  try rhs(&value)
  return value
}

It does compile fine, it also does work as expected when running the app with Xcode on your device, but a release archive will crash badly if T as a class type.

Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
Exception Subtype: KERN_INVALID_ADDRESS at 0x0000000000000358
VM Region Info: 0x358 is not in any region.  Bytes before following region: 4295654568
      REGION TYPE                      START - END             [ VSIZE] PRT/MAX SHRMOD  REGION DETAIL
      UNUSED SPACE AT START
--->  
      __TEXT                 00000001000a8000-00000001000ac000 [   16K] r-x/r-x SM=COW  ...pp/projectname]

Termination Signal: Segmentation fault: 11
Termination Reason: Namespace SIGNAL, Code 0xb
Terminating Process: exc handler [0]
Triggered by Thread:  0

Obviously this is a compiler bug, but hey with the AnyValue I speak about I would be at least save if I had two separate <- functions where one of those is constrained to AnyObject and the other to AnyValue.

Well to be totally fair I might want a different AnyValue then you do, but that's okay. The issue is that we probably won't get this constraint any time soon if we pursue it to implicitly imply value semantics because it isn't a trivial thing to accomplish.

I’m not opposed to this on principle. I’m only opposed until compelling use cases can be demonstrated. AnyObject carries with it semantics and capabilities that allow you to write interesting code you couldn’t without it. This does not appear to be true of AnyValue despite significant discussion in the past.

The crash you’re seeing with the above code might be a bug. The fact that it works in debug builds makes me think it could be an optimization issue. Have you filed a ticket for it?

1 Like

Offtopic:

I discussed the issue with some other people and the also assumed this could be an issue where the optimizer has been too aggressive. In fact this code does run even if you build it for release directly on your device but crashes when you install the app from an Xcode archive.

To answer your question: not yet I had no time to create a small project which can reproduce the issue, but I probably will in the next few days.


Edit:

@anandabits retrofitting AnyValue at this stage with implicit value semantics would be a huge breaking change no? I don't think every value type out there does follow value semantics.

I think the idea about structs having reference-semantics overlaps with move-only types (e.g. IIRC, move-only structs would be allowed to have deinitializers, and I think they can mutate even from non-mutating methods, at least conceptually).

Maybe the other side of the story (classes with value semantics) would take time.

I don't really know enough about this area; I'm going to have to read the ownership manifesto a few more times, but I think there is some overlap between move-only and Copyable types and value/reference semantics.

Indeed, using class as a protocol requirement/conformance was deprecated by a previous proposal, but it’s not implemented yet. I’ve opened an issue in the bug tracker. @jrose , do you have any news?

You've nicely described what you think would be a good addition to Swift, but haven't touched on why they'd fit into the language. Could you describe the problems you're encountering that AnyValue and Immutable are trying to solve?

: class and : AnyObject now parse the same. It would be trivial to emit a warning when we see the former. Would be a good starter bug for someone who wants to learn.

I've previously suggested that protocols with mutating requirements should implicitly be struct/enum-only (or alternatively, class implementations should be able to reassign self). If both a class and a struct can conform to a protocol with a mutating method, generic users of that protocol cannot know whether the method will—or even can—provide value semantics for that method.

Value semantics is something that cannot currently be guaranteed by the language at all. It can at best be a documented semantic requirement. I don’t think changes like preventing classes from conforming to protocols with mutating requirements or introducing AnyValue are well motivated by an argument based on value semantics. They may be worthwhile changes but only if they can be motivated by other use cases.

1 Like

Doesn't a class with only let properties follow value semantics?

No because those constants could be other objects that can mutate. As long the the whole object never mutates than the object has value semantics (e.g. UIColor).

That depends on how value semantics is defined and it actually hasn't been precisely defined yet. Informally most people use a definition that accepts immutable classes as having value semantics.

However, I think there is a good case to be made that value semantics implies locality of reasoning. The possibility of weak and unowned references to any instance of any class in Swift means that really isn't a way to guarantee locality of reasoning about code that works with class instances. We will need to consider this fact very carefully if / when support for value semantics is added.

It's also worth mentioning that when you dig into the details it becomes quite difficult to specify value semantics precisely at the type level rather than the operation level. I spent quite a bit of time trying to do this last summer and was unsuccessful.