It seems like we really need an #include preprocessor directive?


(Edward Connell) #1

Observations about difining an object

   - Structs can't inherit and classes shouldn't inherit, final concrete
   types should be flat
   - Protocols need to be adopted by the final concrete type, otherwise
   constraint specializations aren't correctly applied

This creates a really ugly code duplication situation when you have
multiple object variants that adopt the same protocols.

The storage declaration, common initialization, common didSet, etc... for
protocol member variables must be duplicated for every instance.
If I have 20 objects that have the same base set of 15 vars, this turns
into a maintenance/bug nightmare.

I noticed the use of GYB in the swift source code.
I don't want to involve python in anything I do, and I don't want to mess
up my code/build with all that stuff.

It seems that an easy workaround would be an #include statement.
The boiler plate can be put in a separate file and included wherever it's
needed.
One piece of code, one tool, one set of bugs.

Has this been considered? Or is there a better way to handle this problem.

Thanks, Ed


(Dave Reed) #2

Without seeing the exact problem you're working on, I can't say for certain, but this does seem like a very reasonable case for using inheritance. So I disagree with your statement that "classes shouldn't inherit". Yes, if your writing a framework, you might decide that some classes should not be subclassed publicly (that was the entire argument about making final be the default in Swift), but there's nothing wrong with still using inheritance for those classes in your framework with only the deepest subclass being public and then also marked final so it can't be subclassed.

I suppose you could argue that you want a value type (instead of a reference type) so you want to use a struct and can't accomplish this with a struct other than by repeating the same 15 vars and there's no way to "automate" that. If that's the case, I guess I would argue the way to do it (which would require an enhancement to Swift) would be to allow protocols (or a variation on protocols) to define instance variables (vs. just requiring that the exist right now by saying it must have a getter and possibly setter). I'm by no means a language design expert so I don't know what other problems this might cause but I don't think you're going to see any kind of #include statement in Swift.

Another option I might see some use for "partial inheritance" with structs where the compiler would always know the exact type and determine which methods would be called (i.e., no runtime vtable or objc_msgsend - it would all be done statically by the compiler but you could declare one struct by building upon another struct.

For example

struct Foo {
  var x = 0
}

struct Bar: Foo { // so Bar also has an x instance variable
   var y = 0
}

var b = Bar()
b.x = 3
b.y = 4

// but you couldn't do
var f: Foo = b // since Foo doesn't have a y

Again, I haven't fully thought through the implications and don't have enough language design experience to know what other problems this might cause. And I don't know if this is worth the increased cognitive load of having to check multiple structs like you do with class inheritance to see what methods/variables are defined in base classes.

But all I can say, is I don't see a good solution for your problem (other than using inheritance) in the current version of Swift. I probably would declare a protocol for those 15 variables and have each struct conform to that protocol so you make certain you don't forget one of them in one of the structs but that doesn't write the declarations for you.

Dave Reed

···

On Mar 11, 2017, at 3:12 PM, Edward Connell via swift-users <swift-users@swift.org> wrote:

Observations about difining an object
  • Structs can't inherit and classes shouldn't inherit, final concrete types should be flat
  • Protocols need to be adopted by the final concrete type, otherwise constraint specializations aren't correctly applied
This creates a really ugly code duplication situation when you have multiple object variants that adopt the same protocols.

The storage declaration, common initialization, common didSet, etc... for protocol member variables must be duplicated for every instance.
If I have 20 objects that have the same base set of 15 vars, this turns into a maintenance/bug nightmare.

I noticed the use of GYB in the swift source code.
I don't want to involve python in anything I do, and I don't want to mess up my code/build with all that stuff.

It seems that an easy workaround would be an #include statement.
The boiler plate can be put in a separate file and included wherever it's needed.
One piece of code, one tool, one set of bugs.

Has this been considered? Or is there a better way to handle this problem.

Thanks, Ed


(Jens Alfke) #3

IMHO this seems like a surefire ‘design smell’ — if you have a use case where you find yourself wanting a preprocessor to eliminate lots of redundant lines in your source code, then either your code or the language you’re using have serious problems.

(I see absolutely nothing wrong with inheritance, and it solves exactly this sort of problem. Yes, structs can’t inherit, but they can contain a common struct as a member, which is quite similar and addresses this issue.)

—Jens

···

On Mar 11, 2017, at 12:12 PM, Edward Connell via swift-users <swift-users@swift.org> wrote:

It seems that an easy workaround would be an #include statement.
The boiler plate can be put in a separate file and included wherever it's needed.


(Rien) #4

Observations about difining an object
  • Structs can't inherit and classes shouldn't inherit, final concrete types should be flat

Why?
I always default to the position that we should do what works. Get the project done.
Sure sometimes we do become language lawyers, but don’t let that force you into a corner.
If you need inheritance, use it. AFAIIC OOP is a first class citizen in Swift.

  • Protocols need to be adopted by the final concrete type, otherwise constraint specializations aren't correctly applied
This creates a really ugly code duplication situation when you have multiple object variants that adopt the same protocols.

The storage declaration, common initialization, common didSet, etc... for protocol member variables must be duplicated for every instance.
If I have 20 objects that have the same base set of 15 vars, this turns into a maintenance/bug nightmare.

What about mimicking inheritance?:

struct FooBar {
  var foo: Int
  var bar: Int
}

protocol FooBarManipulation {
  var fooBar: FooBar { get set }
  func doSomethingWithFooBar()
}

extension FooBarManipulation {
   func doSomthingWithFooBar() { … }
}

class SomeClass: FooBarManipulation {
  var fooBar: FooBar?
}

struct SomeStruct: FooBarManipulation {
  var fooBar = FooBar(...)
}

Regards,
Rien.

···

On 11 Mar 2017, at 21:12, Edward Connell via swift-users <swift-users@swift.org> wrote:

I noticed the use of GYB in the swift source code.
I don't want to involve python in anything I do, and I don't want to mess up my code/build with all that stuff.

It seems that an easy workaround would be an #include statement.
The boiler plate can be put in a separate file and included wherever it's needed.
One piece of code, one tool, one set of bugs.

Has this been considered? Or is there a better way to handle this problem.

Thanks, Ed
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(David Sweeris) #5

That forces you into reference semantics, though.

- Dave Sweeris

···

On Mar 11, 2017, at 22:08, Jens Alfke via swift-users <swift-users@swift.org> wrote:

(I see absolutely nothing wrong with inheritance, and it solves exactly this sort of problem. Yes, structs can’t inherit, but they can contain a common struct as a member, which is quite similar and addresses this issue.)


(Jens Alfke) #6

No it doesn’t. A struct member of a struct is still a value. If you put a common class instance in a struct, that would be reference semantics.

—Jens

···

On Mar 11, 2017, at 10:21 PM, David Sweeris <davesweeris@mac.com> wrote:

(I see absolutely nothing wrong with inheritance, and it solves exactly this sort of problem. Yes, structs can’t inherit, but they can contain a common struct as a member, which is quite similar and addresses this issue.)

That forces you into reference semantics, though.


(Edward Connell) #7

Putting a struct with your common vars in it seems like it wouldn't work,
because now it is a member and the vars aren't exposed on your object
directly, so the outer struct wouldn't conform to the protocols.

My original problem with inheritance is that you can't correctly adopt a
protocol on a base class because protocol extensions that specialize based
on Self will use the Self of the base, not of the final class.

Inheritance is fine if nothing happens in the base classes except declaring
the vars required by the protocol. However, any protocol extension
functionality picked up might be the wrong code for the final class. The
only way I see for it to come out right is that the protocols have to be
adopted only by the final class/struct.

#include was just a first thought of how to deal with the need to duplicate
the protocol var declarations. I agree, it's not a good way to handle the
problem.

Maybe I misunderstood it's purpose, but it seems the swift team is using
GYB/python to do code generation to deal with code duplication to address
this problem. That seems even messier to me.

···

On Sat, Mar 11, 2017 at 10:29 PM, Jens Alfke <jens@mooseyard.com> wrote:

On Mar 11, 2017, at 10:21 PM, David Sweeris <davesweeris@mac.com> wrote:

(I see absolutely nothing wrong with inheritance, and it solves exactly
this sort of problem. Yes, structs can’t inherit, but they can contain a
common struct as a member, which is quite similar and addresses this issue.)

That forces you into reference semantics, though.

No it doesn’t. A struct member of a struct is still a value. If you put a
common *class* instance in a struct, that would be reference semantics.

—Jens