Live data from Hacker News

Swift: When Unused Code Is a Bug

peripheryapp.com

21–30 of 77 posts

Re: Swift: When Unused Code Is a Bug

#21
post #13

Earlier quoted context omitted.

Oh, that's definitely weird then. Is there an RFC to have that fixed in some way?

I don't think there is. I think the code is functioning as expected — it's just written in a super confusing way. What should probably happen is that the compiler should throw an error about redefining a function in a super classes extension. I'm not a compiler engineer but a check for that seems like it would be intensive unless that information is codified in the AST somehow.

hmph. I agree, the compiler should probably not let you compile in that case. Funny enough this reminds me of "responder chain politics" in dynamically typed languages with duck typing.

Re: Swift: When Unused Code Is a Bug

#22

Earlier quoted context omitted.

I agree. I never worked with swift and had to look up the terminology. Most of what the article refers to maps to terms from C++ (which is already horribly complicated and offers way too many ways to shoot yourself in the foot) or Java. That PWT sounds an awful lot like a vtable. In C++ you mess up by forgetting to add "virtual" (luckily we recently got "override"), but it seems swift's protocols add another dimensio…

As someone who transitioned from Java to C#/.Net to Objective-C and finally to Swift, AMEN TO THAT! I learned not to say anything as apparently every software engineer "generation" needs to follow something "hip" when very young. Some will grow to see the cycle as it repeats itself in front of them, others will die believing "my programming language was the best, you just had to stick to this list of best practices".…

There's an element of this even within the community of a single language, too. E.g., Apple's been doing Swift talks at WWDC where they sort of introduce their blow-your-mind-paradigm-of-the-year: "Protocol Oriented Programming" was two years ago; "Embrace Algorithms (subtitle: delete your for loops)" was this year.

It concerns me because these are such lush, ripe targets for cargoculting. The behavior in this article is problematic exactly because of the "Apple says we must not use inheritance for anything! I can just use a protocol extension; that's totally not inheritance" mindset.

Re: Swift: When Unused Code Is a Bug

#23
Coming from the .NET world, where extension methods are just syntactic sugar for static methods, the "bug" described in the article wasn't surprising to me. However, it did make me stop and think about how the example would look in C#. This was what I came up with:

    public interface IGreeter {
    }
    
    public static class GreeterExtensions {
        public void Greet(this IGreeter greeter) {
            // If you have CA turned on,
            // you'll get a warning for not
            // using the parameter "greeter"
            Console.WriteLine("Hello, World!");
        }
    }
    
    public class BaseGreeter : IGreeter {
        public abstract void Greet();
    }
    
    public class LazyGreeter : BaseGreeter {
        public override void Greet() {
            Console.WriteLine("sup");
        }
    }
    
    IGreeter greeter = new LazyGreeter();
    
    // This has to be GreeterExtensions.Greet(greeter),
    // because IGreeter is an empty interface
    greeter.Greet();

I think the syntax in C# makes it clearer that you're doing something funky mixing inheritance with extension methods. If you wanted to do it the "correct" way, I think it would look something like:

    public interface IGreeter {
        void Greet();
    }
    
    public class BaseGreeter : IGreeter {
        // Edit: abstract method wouldn't be equivalent to example
        // public abstract void Greet();
        public virtual void Greet() {
            Console.WriteLine("Hello, World!");
        }
    }
    
    public class LazyGreeter : BaseGreeter {
        public override void Greet() {
            Console.WriteLine("sup");
        }
    }
    
    IGreeter greeter = new LazyGreeter();
    greeter.Greet();

Which is more complicated than the idiomatic way of providing default behavior with inheritance:

    public class Greeter {
        public virtual void Greet() {
            Console.WriteLine("Hello, World!");
        }
    }
    
    public class LazyGreeter : Greeter {
        public override void Greet() {
            Console.WriteLine("sup");
        }
    }


    Greeter greeter = new LazyGreeter();
    greeter.Greet();

I'm not a huge fan of the Swift syntax; specifically, IMO, the declaration `class BaseGreeter: Greeter {}` hides where the implementation of `greet` is coming from. Without the method declaration it looks like it's being inherited from the protocol extension, when it's really not.

Re: Swift: When Unused Code Is a Bug

#24
Can anyone explain to me what the rationale for this is?

I can understand the advantage of using static dispatch, i.e. Greeter#greet(object), but I assume there is some mechanism that avoids calling Greeter#greet if BaseGreeter would implement greet and call BaseGreeter#greet(object) instead.

Why does extending a class which extends a protocol not make the extending class implement that protocol in Swift?

Re: Swift: When Unused Code Is a Bug

#25
The explanation is counterintuitive and it's one of those many Swift-ish things I've learned to be wary of. I find that Swift tries to be 'all things' and to often I get lost trying to cobble together a mental picture of what a class is supposed to be given all the various ways to annotate it.

Re: Swift: When Unused Code Is a Bug

#26
post #3

Default implementations on protocols in Swift definitely can be a little dangerous. We've shied our team away from doing this unless the protocol is explicitly used as a mix-in type. There's been some proposals around fixing these, one that comes to mind is: https://forums.swift.org/t/introducing-role-keywords-to-redu...

This doesn't seem to be about default implementations on protocols, I guess they do actually do virtual dispatch so you wouldn't hit this bug with default impls. The main issue is that we have an extension method on a type with the same name of a member of that type. So it can always be statically flagged whether or not the method is used anywhere else. This is easily reproducible in any language that supports extension methods (C#, for instance). I wonder if that could be useful at all.

Re: Swift: When Unused Code Is a Bug

#27

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…

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).

Re: Swift: When Unused Code Is a Bug

#28

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…

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).

> infuriating insistence on chasing performance at the expense of developer ergonomics

It would be slightly less infuriating had that chase actually delivered, but it dramatically has not.

Re: Swift: When Unused Code Is a Bug

#29

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…

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 because the messages would be dispatched properly. If this was all new world, you wouldn't even be allowed to subclass, would refer to your trait explicitly, and again, everything would work fine.

This is a fundamental structural issue with the language, philosophically, and will definitely cost programmer productivity. I'd argue this isn't a problem so much as behaves correctly due to insufficient planning.

It's not that they're taking a position on dynamic dispatch, it's that they're trying to take both positions, likely in support of Objective-C compatibility.

IMO dynamic dispatch has caused way more programmer productivity issues than anything else - you write code the compiler can't validate because you explicitly chose not to give it the information it needs to do it's job. It can't tell you your code is right. Nothing really can. The new world is a better place, Go and Rust have it right, inheritance is dead.

Re: Swift: When Unused Code Is a Bug

#30
post #16
post #9

If I saw this in a code review, I would flag it for re-design. Mixing composition and inheritance especially in this way is definitely going to confuse anyone who doesn't read every line to figure out what's going on.

I don't think there's anything wrong with mixing composition and inheritance per se , if everything is obeying strict OOP principles (Open-Closed, etc.) If the mix-in was adding functionality to the base class, and then the subclass was there to specialize the behavior of the base class without breaking any of the contracts the base class itself makes (i.e. any unit test that works on a Greeter should work on a LazyG…

Yeah, A static analyzer should be able to figure out what's going on and throw an error.
Post reply on HN