Live data from Hacker News

Swift: When Unused Code Is a Bug

peripheryapp.com

51–60 of 77 posts

Re: Swift: When Unused Code Is a Bug

#51
This is my favorite Swift gotcha :D

This bug bit me one time back before remote debugging came out when I was developing an app for a device that plugged into the lightning port. The only way to debug was to send log messages over the network to a log server running on my laptop, and the connected device sent messages to satellites where there was often a 10-minute gap in connectivity. And I couldn’t reproduce it in tests. That took some time to diagnose!

I like to bring this issue up if an interview candidate seems extremely bought in to protocol-driven development. I wouldn’t be surprised if any level of dev wasn’t aware of it, but would be impressed if anybody identified it.

Re: Swift: When Unused Code Is a Bug

#52

Wow, Heisen-Swift, where the Heisenbugs are in the language specification! What an object is, which is roughly equivalent to its observed behavior, should never depend on how it is declared. let greeter = LazyGreeter() let greeter1: Greeter = greeter print(greeter) print(greeter1) greeter.greet() greeter1.greet() UPDATE: just in case it's not clear, this prints the following: greeter.LazyGreeter greeter.LazyGreeter s…

> What an object is, which is roughly equivalent to its observed behavior, should never depend on how it is declared. This is actually a statement that strong typing should not exist. A "declaration" (really a type declaration) indicates the type, and if observed behavior cannot depend on the types there is not much point to types. Or stated positively: in a strongly typed language, the types are part of 'what an obj…

> This is actually a statement that strong typing should not exist.

Nope. It is a statement that objects shouldn't be different depending on how you look at them.

The example is a class, so a reference type. That means that greeter and greeter1 are just two references to the exact same underlying object.

Your example are two distinct value type instances that you happened to initialize from the same literal.

So not even close to comparable situations. Speaking of comparable:

> Both a and b are "the same" value, insofar as they are `==`

Also nope.

   let a: Int = 3
   let b: Float = 3

   let r=a==b
   print(r)
Let's compile this:

   swiftc numbers.swift 
   numbers.swift:4:8: error: binary operator '==' cannot be applied to operands of type 'Int' and 'Float'
   let r=a==b
         ~^ ~
   numbers.swift:4:8: note: expected an argument list of type '(Int, Int)'
   let r=a==b
          ^

Re: Swift: When Unused Code Is a Bug

#53

Earlier quoted context omitted.

It seems this is what you get when you decide dynamic dispatch is a demon that must be cast out. :/ It's a good example of Swift's at-times-infuriating insistence on chasing performance at the expense of developer ergonomics. That said, I'm sure it will be mediated eventually (although probably with more compiler annotations).

The problem here is that Swift is sort of this hybrid between two disparate worldviews, the old Objective-C world where inheritance is cool and dispatch is dynamic and the new world where inheritance is not even permitted and traits are used to provide dynamic dispatch. It's trying to be everything to everyone. This is one of those crossover points where the seams don't meet. If this was all dynamic, it would be fine…

I guess fundamentally, this is the question I have about this situation: would you ever, in real code, want dispatch to go straight back to the default implementation when you have in hand a value with a custom implementation?

I may be having a failure of imagination; but I certainly haven't ever seen a case where I'd want that.

Re: Swift: When Unused Code Is a Bug

#54

Earlier quoted context omitted.

The problem here is that Swift is sort of this hybrid between two disparate worldviews, the old Objective-C world where inheritance is cool and dispatch is dynamic and the new world where inheritance is not even permitted and traits are used to provide dynamic dispatch. It's trying to be everything to everyone. This is one of those crossover points where the seams don't meet. If this was all dynamic, it would be fine…

This is far too far, and a lot of it is wrong

Yeah I’d love to have a conversation about your thoughts here!

Re: Swift: When Unused Code Is a Bug

#55

Earlier quoted context omitted.

The problem here is that Swift is sort of this hybrid between two disparate worldviews, the old Objective-C world where inheritance is cool and dispatch is dynamic and the new world where inheritance is not even permitted and traits are used to provide dynamic dispatch. It's trying to be everything to everyone. This is one of those crossover points where the seams don't meet. If this was all dynamic, it would be fine…

I guess fundamentally, this is the question I have about this situation: would you ever, in real code, want dispatch to go straight back to the default implementation when you have in hand a value with a custom implementation? I may be having a failure of imagination; but I certainly haven't ever seen a case where I'd want that.

I’d imagine the issue lies in Swift classes utilizing only static dispatch. The protocol conformance creates a witness table populated by the methods of the class in question, but as Swift classes have no vtables except in ObjC compatability mode (right?) there’s no way to pass down the invocation from the protocol witness to a specific subclass as the method can not be resolved at runtime. This is the difference between the dynamic dispatch via witness table and the explicit static dispatch to the subclass when type information is available.

Thus it comes down to dynamic dispatch always (or at least for anything that has a protocol conformance at all) vs surprising behavior here.

I do agree with you, however.

Re: Swift: When Unused Code Is a Bug

#56

Earlier quoted context omitted.

> What an object is, which is roughly equivalent to its observed behavior, should never depend on how it is declared. This is actually a statement that strong typing should not exist. A "declaration" (really a type declaration) indicates the type, and if observed behavior cannot depend on the types there is not much point to types. Or stated positively: in a strongly typed language, the types are part of 'what an obj…

> This is actually a statement that strong typing should not exist. Nope. It is a statement that objects shouldn't be different depending on how you look at them. The example is a class, so a reference type. That means that greeter and greeter1 are just two references to the exact same underlying object. Your example are two distinct value type instances that you happened to initialize from the same literal. So not e…

You may not be familiar with swift but a protocol isn’t necessarily a class. Structs ( so, value type) can also implements a protocol, and so in effect you can not tell a lot about what you’re manipulating, beyond what’s declared at the protocol level.

Re: Swift: When Unused Code Is a Bug

#57

Wow, Heisen-Swift, where the Heisenbugs are in the language specification! What an object is, which is roughly equivalent to its observed behavior, should never depend on how it is declared. let greeter = LazyGreeter() let greeter1: Greeter = greeter print(greeter) print(greeter1) greeter.greet() greeter1.greet() UPDATE: just in case it's not clear, this prints the following: greeter.LazyGreeter greeter.LazyGreeter s…

> What an object is, which is roughly equivalent to its observed behavior, should never depend on how it is declared. This is actually a statement that strong typing should not exist. A "declaration" (really a type declaration) indicates the type, and if observed behavior cannot depend on the types there is not much point to types. Or stated positively: in a strongly typed language, the types are part of 'what an obj…

a and b are not the same value, and don't model the same underlying concept.

a: Int would be 0x0000000000000003.

b: Float would be 0x40400000.

While both are specified literally as "3" the compiler de-sugars that into the values above, and further, the type system takes that knowledge into consideration to prevent you from doing things that don't make sense like trying to equate "a" and "b" without explicitly performing a lossy conversion (Binary operator '==' cannot be applied to operands of type 'Float' and 'Int').

I don't believe this to be a name resolution issue, but rather a protocol conformance creates a vtable for the protocol but beyond that dynamic dispatch doesn't exist so it's not possible to resolve the actual method overridden in the subclass.

This isn't a statement on typing (other than your example actually making a solid case for strong typing); rather the boundary between static and dynamic dispatch is not well-constructed in the case of protocol conformances.

Re: Swift: When Unused Code Is a Bug

#58
post #56

Earlier quoted context omitted.

> This is actually a statement that strong typing should not exist. Nope. It is a statement that objects shouldn't be different depending on how you look at them. The example is a class, so a reference type. That means that greeter and greeter1 are just two references to the exact same underlying object. Your example are two distinct value type instances that you happened to initialize from the same literal. So not e…

You may not be familiar with swift but a protocol isn’t necessarily a class. Structs ( so, value type) can also implements a protocol, and so in effect you can not tell a lot about what you’re manipulating, beyond what’s declared at the protocol level.

From TFA:

   class BaseGreeter: Greeter {}

   class LazyGreeter: BaseGreeter {

Re: Swift: When Unused Code Is a Bug

#59

Earlier quoted context omitted.

> What an object is, which is roughly equivalent to its observed behavior, should never depend on how it is declared. This is actually a statement that strong typing should not exist. A "declaration" (really a type declaration) indicates the type, and if observed behavior cannot depend on the types there is not much point to types. Or stated positively: in a strongly typed language, the types are part of 'what an obj…

> This is actually a statement that strong typing should not exist. Nope. It is a statement that objects shouldn't be different depending on how you look at them. The example is a class, so a reference type. That means that greeter and greeter1 are just two references to the exact same underlying object. Your example are two distinct value type instances that you happened to initialize from the same literal. So not e…

> That means that greeter and greeter1 are just two references to the exact same underlying object

They are two references that differ in type. It is a feature in Swift (and any strongly-typed language) that references are typed and when types differ, behaviors can differ. I understand you disagree with this design principle but it is an inherent property of strong type systems that have reference semantics.

> two distinct value type instances

“Nope.” There isn’t any such thing as a “value type instance” since instances are a semantic of reference types.

> objects shouldn’t be different depending on how you look at them

The “difference” here is only in the types, so this statement is equivalent to “references shouldn’t be different depending on their type.” This statement implies that types should be useless

Re: Swift: When Unused Code Is a Bug

#60

Earlier quoted context omitted.

The problem here is that Swift is sort of this hybrid between two disparate worldviews, the old Objective-C world where inheritance is cool and dispatch is dynamic and the new world where inheritance is not even permitted and traits are used to provide dynamic dispatch. It's trying to be everything to everyone. This is one of those crossover points where the seams don't meet. If this was all dynamic, it would be fine…

While inheritance is OK, and useful in programming-by-difference scenarios, Objective-C style polymorphism has always de-emphasized inheritance. Dynamic dispatch is central to both the most productive and largest-scale software systems/environments in the world. If you think the compiler can tell you everything, you're in for a world of hurt, and if you think you can statically type-check the world: good luck. Again,…

Objective-C deemphasizes formal inheritance. Class clusters and informal protocols generally substitute for some of the benefits that inheritance can give.
Post reply on HN