Pre proposal: Should there be a way to distinguish class instances from struct instances


(Daniel Steinberg) #1

The way in which we reason about instances of value types and instances of reference types is significantly different and yet there is no way to easily distinguish them in Swift.

In Objective-C, for example, if we have an NSDate and an NSTimeInterval we can tell that NSDate is a reference type and NSTimeInterval is a value type by looking at how it is used in code

NSDate *myDate = //…
NSTimeInterval someTimeInterval = //…

The “*” helps us see that myDate is a pointer to an instance of NSDate.

We see this in methods that return values as well

- (NSString *)aStrringReturningMethod // …
- (NSInteger)numberOfMistakesInThisEmail //...

I realize that this is a result of Objective-C living in C’s world and we don’t have that constraint in Swift.

However, this means that when the semantics of variables, parameters, properties, and return values from methods is not always clear in our code. Because structs can have methods in Swift it is all to easy to confuse an instance of a struct with an instance of a class.

I may be alone here, but I think it would be less confusing if there were some way to distinguish between value types and reference types in code.

Thank you,

Daniel


(Sean Kosanovich) #2

I can definitely see value in knowing if you’re dealing with a Value or Reference Type. Of course, the easy answer is to just look at the documentation/definition, but that is slow.

Likewise, pointer notation feels old and looks messy. I am not sure of a great way to show this, any suggestions?

···

On Dec 17, 2015, at 7:23 AM, Daniel Steinberg via swift-evolution <swift-evolution@swift.org> wrote:

The way in which we reason about instances of value types and instances of reference types is significantly different and yet there is no way to easily distinguish them in Swift.

In Objective-C, for example, if we have an NSDate and an NSTimeInterval we can tell that NSDate is a reference type and NSTimeInterval is a value type by looking at how it is used in code

NSDate *myDate = //…
NSTimeInterval someTimeInterval = //…

The “*” helps us see that myDate is a pointer to an instance of NSDate.

We see this in methods that return values as well

- (NSString *)aStrringReturningMethod // …
- (NSInteger)numberOfMistakesInThisEmail //...

I realize that this is a result of Objective-C living in C’s world and we don’t have that constraint in Swift.

However, this means that when the semantics of variables, parameters, properties, and return values from methods is not always clear in our code. Because structs can have methods in Swift it is all to easy to confuse an instance of a struct with an instance of a class.

I may be alone here, but I think it would be less confusing if there were some way to distinguish between value types and reference types in code.

Thank you,

Daniel
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Dave Abrahams) #3

The real differences happen whenever you mutate them by part (assignment acts the same), which suggests that they should have different syntax for member access, e.g. someObject->mutatingMethod() vs. someValue.mutatingMethod().

Personally, I like that sort of thing. Not to discourage you, but I think most people don’t recognize that types with value semantics are fundamentally different from those with reference semantics, e.g. avoiding a syntactic barrier between them was an explicit design goal of Swift 1. Also I don’t think you’d find much support for even a 1-character penalty on method calls and property accesses to classes. So, while I think we ought to do something in this direction, I expect it to be hard-won.

-Dave

···

On Dec 17, 2015, at 4:23 AM, Daniel Steinberg via swift-evolution <swift-evolution@swift.org> wrote:

The way in which we reason about instances of value types and instances of reference types is significantly different and yet there is no way to easily distinguish them in Swift.

In Objective-C, for example, if we have an NSDate and an NSTimeInterval we can tell that NSDate is a reference type and NSTimeInterval is a value type by looking at how it is used in code

NSDate *myDate = //…
NSTimeInterval someTimeInterval = //…

The “*” helps us see that myDate is a pointer to an instance of NSDate.

We see this in methods that return values as well

- (NSString *)aStrringReturningMethod // …
- (NSInteger)numberOfMistakesInThisEmail //...

I realize that this is a result of Objective-C living in C’s world and we don’t have that constraint in Swift.

However, this means that when the semantics of variables, parameters, properties, and return values from methods is not always clear in our code. Because structs can have methods in Swift it is all to easy to confuse an instance of a struct with an instance of a class.

I may be alone here, but I think it would be less confusing if there were some way to distinguish between value types and reference types in code.


(James Dempsey) #4

Daniel,

It occurs to me that if there was such a thing in Swift, the distinction would be lost in any code that used protocols for typing since both value and reference types can implement the same protocol.

That doesn’t mean what you suggest would not be useful, just adding for consideration that there is a significant use case that potentially would not be covered.

James

···

On Dec 17, 2015, at 4:23 AM, Daniel Steinberg via swift-evolution <swift-evolution@swift.org> wrote:

The way in which we reason about instances of value types and instances of reference types is significantly different and yet there is no way to easily distinguish them in Swift.

In Objective-C, for example, if we have an NSDate and an NSTimeInterval we can tell that NSDate is a reference type and NSTimeInterval is a value type by looking at how it is used in code

NSDate *myDate = //…
NSTimeInterval someTimeInterval = //…

The “*” helps us see that myDate is a pointer to an instance of NSDate.

We see this in methods that return values as well

- (NSString *)aStrringReturningMethod // …
- (NSInteger)numberOfMistakesInThisEmail //...

I realize that this is a result of Objective-C living in C’s world and we don’t have that constraint in Swift.

However, this means that when the semantics of variables, parameters, properties, and return values from methods is not always clear in our code. Because structs can have methods in Swift it is all to easy to confuse an instance of a struct with an instance of a class.

I may be alone here, but I think it would be less confusing if there were some way to distinguish between value types and reference types in code.

Thank you,

Daniel
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(ilya) #5

This does not seem very practical as there is a whole continuum of
behaviors between pure value (like Int) and reference (like UIWindow)
types.

Consider for example

NSURL: an object, but immutable
Array: a struct, but doesn't take extra space if copied without mutation
Either<T, U>: an enum is similar to a struct, but may very well wrap a
mutable class instance

···

On Thu, Dec 17, 2015 at 3:23 PM, Daniel Steinberg via swift-evolution < swift-evolution@swift.org> wrote:

The way in which we reason about instances of value types and instances of
reference types is significantly different and yet there is no way to
easily distinguish them in Swift.

In Objective-C, for example, if we have an NSDate and an NSTimeInterval we
can tell that NSDate is a reference type and NSTimeInterval is a value type
by looking at how it is used in code

NSDate *myDate = //…
NSTimeInterval someTimeInterval = //…

The “*” helps us see that myDate is a pointer to an instance of NSDate.

We see this in methods that return values as well

- (NSString *)aStrringReturningMethod // …
- (NSInteger)numberOfMistakesInThisEmail //...

I realize that this is a result of Objective-C living in C’s world and we
don’t have that constraint in Swift.

However, this means that when the semantics of variables, parameters,
properties, and return values from methods is not always clear in our code.
Because structs can have methods in Swift it is all to easy to confuse an
instance of a struct with an instance of a class.

I may be alone here, but I think it would be less confusing if there were
some way to distinguish between value types and reference types in code.

Thank you,

Daniel
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Fastmail) #6

I would be interested in exploring more responsive IDEs that could reveal this information contextually, rather than embedding it within the source files. In the same way we can option click on a decl and see the type, being able to surface that information in a useful manner would be ideal.

···

Sent from my iPhone

On 17 Dec 2015, at 07:52, Sean Kosanovich via swift-evolution <swift-evolution@swift.org> wrote:

I can definitely see value in knowing if you’re dealing with a Value or Reference Type. Of course, the easy answer is to just look at the documentation/definition, but that is slow.

Likewise, pointer notation feels old and looks messy. I am not sure of a great way to show this, any suggestions?

On Dec 17, 2015, at 7:23 AM, Daniel Steinberg via swift-evolution <swift-evolution@swift.org> wrote:

The way in which we reason about instances of value types and instances of reference types is significantly different and yet there is no way to easily distinguish them in Swift.

In Objective-C, for example, if we have an NSDate and an NSTimeInterval we can tell that NSDate is a reference type and NSTimeInterval is a value type by looking at how it is used in code

NSDate *myDate = //…
NSTimeInterval someTimeInterval = //…

The “*” helps us see that myDate is a pointer to an instance of NSDate.

We see this in methods that return values as well

- (NSString *)aStrringReturningMethod // …
- (NSInteger)numberOfMistakesInThisEmail //...

I realize that this is a result of Objective-C living in C’s world and we don’t have that constraint in Swift.

However, this means that when the semantics of variables, parameters, properties, and return values from methods is not always clear in our code. Because structs can have methods in Swift it is all to easy to confuse an instance of a struct with an instance of a class.

I may be alone here, but I think it would be less confusing if there were some way to distinguish between value types and reference types in code.

Thank you,

Daniel
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Matthew Johnson) #7

The way in which we reason about instances of value types and instances of reference types is significantly different and yet there is no way to easily distinguish them in Swift.

In Objective-C, for example, if we have an NSDate and an NSTimeInterval we can tell that NSDate is a reference type and NSTimeInterval is a value type by looking at how it is used in code

NSDate *myDate = //…
NSTimeInterval someTimeInterval = //…

The “*” helps us see that myDate is a pointer to an instance of NSDate.

We see this in methods that return values as well

- (NSString *)aStrringReturningMethod // …
- (NSInteger)numberOfMistakesInThisEmail //...

I realize that this is a result of Objective-C living in C’s world and we don’t have that constraint in Swift.

However, this means that when the semantics of variables, parameters, properties, and return values from methods is not always clear in our code. Because structs can have methods in Swift it is all to easy to confuse an instance of a struct with an instance of a class.

I may be alone here, but I think it would be less confusing if there were some way to distinguish between value types and reference types in code.

The real differences happen whenever you mutate them by part (assignment acts the same), which suggests that they should have different syntax for member access, e.g. someObject->mutatingMethod() vs. someValue.mutatingMethod().

Of course real differences also exist in the possibility that someone else could mutate a shared instance of a reference type behind your back. Representing this difference syntactically requires a type annotation, not just a different syntax for member access. It's also worth noting that value types and non-shared instances of reference types actually have pretty similar semantics.

I would like to think there is a good way to clearly represent these differences in a language as they are quite important. The reality so far has been that attempting to do so introduces quite a bit of complexity. Rust is a current example of this.

My understanding is that an important goal for Swift is to avoid this kind of complexity in the type system and in the syntax. As noted in this thread doing so doesn't make it go away, it just sweeps it under the rug a bit.

If there were a way to surface the important semantic distinctions without making the language terribly complex and without sacrificing semantically valid generic code it would probably be embraced. I think this is just a really hard problem for which we don't yet have a great solution.

···

Sent from my iPad

On Dec 17, 2015, at 3:33 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:

On Dec 17, 2015, at 4:23 AM, Daniel Steinberg via swift-evolution <swift-evolution@swift.org> wrote:

Personally, I like that sort of thing. Not to discourage you, but I think most people don’t recognize that types with value semantics are fundamentally different from those with reference semantics, e.g. avoiding a syntactic barrier between them was an explicit design goal of Swift 1. Also I don’t think you’d find much support for even a 1-character penalty on method calls and property accesses to classes. So, while I think we ought to do something in this direction, I expect it to be hard-won.

-Dave

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Dave Abrahams) #8

Daniel,

It occurs to me that if there was such a thing in Swift, the distinction would be lost in any code that used protocols for typing since both value and reference types can implement the same protocol.

In an ideal world, most protocols-at least those with mutating members- would be constrained to either value or reference-semantic types, as most generic components you can write inevitably make assumptions about the logical (in)dependence of a and b after you execute a = b. I'm sure the world would break if you made a CollectionType whose Index was a class, for example. But we don't currently have a way in the language to represent "has value semantics" (since value types can hold references) or "is constrained to have value semantics".

···

Sent from my moss-covered three-handled family gradunza

On Dec 17, 2015, at 4:26 PM, James Dempsey via swift-evolution <swift-evolution@swift.org> wrote:

That doesn’t mean what you suggest would not be useful, just adding for consideration that there is a significant use case that potentially would not be covered.

James

On Dec 17, 2015, at 4:23 AM, Daniel Steinberg via swift-evolution <swift-evolution@swift.org> wrote:

The way in which we reason about instances of value types and instances of reference types is significantly different and yet there is no way to easily distinguish them in Swift.

In Objective-C, for example, if we have an NSDate and an NSTimeInterval we can tell that NSDate is a reference type and NSTimeInterval is a value type by looking at how it is used in code

NSDate *myDate = //…
NSTimeInterval someTimeInterval = //…

The “*” helps us see that myDate is a pointer to an instance of NSDate.

We see this in methods that return values as well

- (NSString *)aStrringReturningMethod // …
- (NSInteger)numberOfMistakesInThisEmail //...

I realize that this is a result of Objective-C living in C’s world and we don’t have that constraint in Swift.

However, this means that when the semantics of variables, parameters, properties, and return values from methods is not always clear in our code. Because structs can have methods in Swift it is all to easy to confuse an instance of a struct with an instance of a class.

I may be alone here, but I think it would be less confusing if there were some way to distinguish between value types and reference types in code.

Thank you,

Daniel
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution