Live data from Hacker News

Swift: When Unused Code Is a Bug

peripheryapp.com

41–50 of 77 posts

Re: Swift: When Unused Code Is a Bug

#41

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…

Interestingly, in Swift, you Cannot compare `a == b` as the compiler tells you the two types are incompatible.

Re: Swift: When Unused Code Is a Bug

#42
I don't know that you can really call this a bug? It just seems to be the design choice. Though I agree, it seems odd at first, and appears possibly accidental, it could have been on purpose.

My question to swifters: what if LazyGreeter had another method, say lazyGreet, and you typed it as Greeter? Then you called let greeter : Greeter = LazyGreeter(); greeter.lazyGreet();

Would you expect this to work or fail?

I'd expect it to fail.

In that sense, I assume its simply that extension methods override child overrides. And its just something you need to know. That can even have interesting utility in some scenarios.

Re: Swift: When Unused Code Is a Bug

#43
post #35

This is an interesting demonstration of a hard edge of the language. It's not because Swift holds some sort of grudge against dynamic dispatch, but rather because it's trying to reconcile some subtly incompatible design goals: 1. Adding a default implementation for a protocol requirement shouldn't stop existing types that correctly conform to that protocol from compiling. This is a reasonable user expectation and mak…

> Adding a default implementation for a protocol requirement shouldn't stop existing types that correctly conform to that protocol from compiling.

I would disagree with that; adding that default implementation is not a simply internal change. It flat-out changes compilibility; it makes invalid code into valid code:

    protocol P {
        func f()
    }

    class C : P {}    // Error!
---

    protocol P {
        func f()
    }
    extension P {
        func f() {}
    }

    class C : P {}    // Okay
Given the trap demonstrated here, the converse should hold as well. Doubly so in light of the ways the compiler (mostly helpfully) enforces various aspects of subclassing, as you pointed out.

In many ways, protocols (right now at least) feel like Swift having its cake but not eating it: strictness floats around them in ways that do not help developers (ugh, PATs!) but is absent where it would:

    // This all compiles without any warnings?!
    protocol P {
        var i: Int { get set }
    }

    protocol Q : P {
        var i: Int { get }
    }

    class C : Q, P {
        var i = 10
    }

    // Now make a protocol that inherits from another,
    // where both are class/AnyObject constrained
    
    //---

    struct S : Hashable {    // Swift 4.1, synthesized 
        let s: String
    }
    extension S : Equatable {
        static func == (lhs: S, rhs: S) -> Bool {
            // Hashable semantics, smashable semantics. Hold my beer.
            return lhs.s.first == rhs.s.first
        }
    }

Re: Swift: When Unused Code Is a Bug

#44
Another "unused code bug" would be when a locally scoped variable which is not supposed to be there shadows the intended global variable.

It's just a matter of global scoping and precedence. But yeah, sharing notation between inheritance and extension is a tad bit reductionist.

Re: Swift: When Unused Code Is a Bug

#45
post #42

I don't know that you can really call this a bug? It just seems to be the design choice. Though I agree, it seems odd at first, and appears possibly accidental, it could have been on purpose. My question to swifters: what if LazyGreeter had another method, say lazyGreet, and you typed it as Greeter? Then you called let greeter : Greeter = LazyGreeter(); greeter.lazyGreet(); Would you expect this to work or fail? I'd…

In python, `greeter.lazyGreet()` would succeed, because python only has types at runtime. If you used mypy, you would get a warning for this. No warning would be issued for `greeter.greet()`, but since function dispatch is still done at runtime, this would call `LazyGreeter.greet()`.

I concur that the behaviour is a design decision, not a bug, but I think most programmers would in general prefer design decisions to bring the language closer to python.

Re: Swift: When Unused Code Is a Bug

#46
post #37

I'm reading the comments here about how the languages fails, or what it should have done better, but at the same time I'm feeling the same as I do when building up something with Kubernetes and the problem isn't really the language, it's the shortcuts we take with it to stay concise. e.g. in Kubernetes you can give a Pod, Deployment, Service, Ingress, Volume, etc. etc. the exact same name and in fact many examples en…

You make a fair point, but isn't it part of a platform's responsibility to not make footguns quite so easy to grab? Especially given Swift's general (and mostly helpful) attitude of insistence on safe, explicit constructs. For example, it's a warning to ignore a function call result; it's an error to not chain up to a superclass's initializer; it's an error to re-implement a class member without the `override` keywor…

The problem with this angle is that while sure, you can always make a compiler more intelligent in ambiguous situations, you must also now train all the humans interfacing with it to be equally as intelligent (or at least, able to sufficiently disambiguate).

We don't see many context-sensitive grammars for programming languages, not because very difficult to implement, but I suspect because they're hard for humans to understand.

There is something to be said for the simplicity of naming things to describe what they are (e.g. Hungarian notation), but I really think it's a mistake for languages (configuration, programming, etc.) to even allow identifiers to be reused for differing types.

Re: Swift: When Unused Code Is a Bug

#47
post #37

I'm reading the comments here about how the languages fails, or what it should have done better, but at the same time I'm feeling the same as I do when building up something with Kubernetes and the problem isn't really the language, it's the shortcuts we take with it to stay concise. e.g. in Kubernetes you can give a Pod, Deployment, Service, Ingress, Volume, etc. etc. the exact same name and in fact many examples en…

> So it is with this Swift example and having a protocol, an extension, and a base class all declaring the exact same thing.

I'm not sure what you mean. The only thing that's named the same is 'greet', and of course that has to be the same because it's the thing you're implementing.

Edit: Perhaps the protocol and extension could have different names? I don't see how that would make the behavior clearer.

Re: Swift: When Unused Code Is a Bug

#48

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…

Haskell has no problem with this(though its default Num hierarchy is sorely lacking)

    Prelude> a = 1
    Prelude> a/2
    0.5
    Prelude> :t a/2
    a/2 :: Fractional a => a
    Prelude> b = 1 :: Int
    Prelude> b/2

    :6:1: error:
        • No instance for (Fractional Int) arising from a use of ‘/’
        • In the expression: b / 2
          In an equation for ‘it’: it = b / 2
    Prelude> b `div` 2
    0
    Prelude> :t div
    div :: Integral a => a -> a -> a

Re: Swift: When Unused Code Is a Bug

#49

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…

Your example does not have the same structure; `Int` and `Float` are concrete types, not interface types.

> strong typing

Strong typing is not the same as static typing. `let greeter: Greeter = LazyGreeter()` is a strongly-typed `LazyGreeter`: you cannot use it where a value of another incompatible type is required, nor can you change its runtime type. But its static type is `Greeter`. Swift uses the latter for method resolution, but there's nothing inevitable or inherent about that.

`NSArray * array = [NSArray new];` is statically typed as an `NSArray`, but its dynamic (and strong) type is `__NSArray0`, and that's where the value's implementations come from.

> they model the same underlying mathematical concept

They don't: one models an integral and the other models(/approximates) a real. Which is why, as another commentor already pointed out, they're not `==`.

Re: Swift: When Unused Code Is a Bug

#50

Earlier quoted context omitted.

You make a fair point, but isn't it part of a platform's responsibility to not make footguns quite so easy to grab? Especially given Swift's general (and mostly helpful) attitude of insistence on safe, explicit constructs. For example, it's a warning to ignore a function call result; it's an error to not chain up to a superclass's initializer; it's an error to re-implement a class member without the `override` keywor…

The problem with this angle is that while sure, you can always make a compiler more intelligent in ambiguous situations, you must also now train all the humans interfacing with it to be equally as intelligent (or at least, able to sufficiently disambiguate). We don't see many context-sensitive grammars for programming languages, not because very difficult to implement, but I suspect because they're hard for humans to…

> make a compiler more intelligent in ambiguous situations, [but] you must also now train all the humans interfacing with it to be equally as intelligent

I'm not sure what you mean here; probably I'm misunderstanding you somehow.

If you make the compiler more intelligent so that it can tell the humans about subtle problems, that lets the humans be less intelligent (or at least have to think less). No?

Post reply on HN