Pitch: Limit typealias extensions to the typealias


(Yvo van Beek) #1

Typealiases can greatly reduce the complexity of code. But I think one
change in how the compiler handles them could make them even more powerful.

Let's say I'm creating a web server framework and I've created a simple
dictionary to store HTTP headers (I know that headers are more complex than
that, but as an example). I could write something like this:

    typealias HeaderKey = String

  var headers = [HeaderKey: String]()
  headers["Host"] = "domain.com"

Now I can define a couple of default headers like this:

  extension HeaderKey {
    static var lastModified: String { return "Last-Modified" }
    static var host: String { return "Host" }
  }

After that I can do this:

  var headers = [HeaderKey: String]()
  headers[.host] = "domain.com"
  headers[.lastModified] = "some date"
  headers["X-MyHeader"] = "This still works too"

But unfortunately the extension is also applied to normal strings:

    var normalString: String = .host

Perhaps it would be better if the extension would only apply to the parts
of my code where I use the HeaderKey typealias and not to all Strings. This
could be a great tool to specialize classes by creating a typealias and
adding functionality to it. Another example I can think of is typealiases
for dictionaries or arrays with added business logic through extensions
(especially since you can't inherit from structs).

If you want to create an extension that adds functionality to all Strings
you could have created an extension for String instead of HeaderKey.

Please let me know what you think. I'm not sure how complex this change
would be.
I could write a proposal if you're interested.

Kind regards,
Yvo


(Jacob Williams) #2

+1 from me.

There have been times I’ve wanted to subclass an object (such as String) but since it is a non-class, non-protocol type you can only extend Strings existing functionality which adds that same functionality to Strings everywhere. It would be nice if we could either extend type aliases (and only the type alias), or if it were possible to inherit from structs so that we could create a custom string type like so:

struct HeaderKey: String {
  static var lastModified: String { return “Last-Modified” }
  static var host: String { return “Host” }
}

I realize that struct inheritance is far less likely, since that defeats one of the main pieces of what makes a struct a struct. So I’m all for this proposal of allowing type aliases to be extended as though they were their own struct/class.

Unfortunately, I’m not sure how feasible this kind of functionality would actually be, but if it’s possible then I’m in favor of implementing it.

···

On Jun 8, 2017, at 10:14 PM, Yvo van Beek via swift-evolution <swift-evolution@swift.org> wrote:

Typealiases can greatly reduce the complexity of code. But I think one change in how the compiler handles them could make them even more powerful.

Let's say I'm creating a web server framework and I've created a simple dictionary to store HTTP headers (I know that headers are more complex than that, but as an example). I could write something like this:

    typealias HeaderKey = String

  var headers = [HeaderKey: String]()
  headers["Host"] = "domain.com <http://domain.com/>"

Now I can define a couple of default headers like this:

  extension HeaderKey {
    static var lastModified: String { return "Last-Modified" }
    static var host: String { return "Host" }
  }

After that I can do this:

  var headers = [HeaderKey: String]()
  headers[.host] = "domain.com <http://domain.com/>"
  headers[.lastModified] = "some date"
  headers["X-MyHeader"] = "This still works too"

But unfortunately the extension is also applied to normal strings:

    var normalString: String = .host

Perhaps it would be better if the extension would only apply to the parts of my code where I use the HeaderKey typealias and not to all Strings. This could be a great tool to specialize classes by creating a typealias and adding functionality to it. Another example I can think of is typealiases for dictionaries or arrays with added business logic through extensions (especially since you can't inherit from structs).

If you want to create an extension that adds functionality to all Strings you could have created an extension for String instead of HeaderKey.

Please let me know what you think. I'm not sure how complex this change would be.
I could write a proposal if you're interested.

Kind regards,
Yvo
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Will Field-Thompson) #3

I feel like this might be better served by something like newtype which I
know I've seen floating around on this list before. The idea is that
something like:

newtype HeaderKey = String

would serve roughly as syntactic sugar for something like

struct HeaderKey {
   let value: String
   init(_ value: String) { self.value = value }
   /* Possibly insert methods from String here, possibly do something else,
whatever */
}

I've always thought this might be interesting, but there's a few reasons I
suggest it in place of extending a typealias:
1) Backwards compatibility — this could very easily break existing code
2) Semantically it seems to me that an *alias* is a useful distinction
from *"let's
make a new type" *— sometimes you actually just want to call one type by
another name in some context

Regardless, I think you could solve your particular problem like this:

public struct HeaderKey {
   let value: String
   public init(_ value: String) { self.value = value }
}

and conform HeaderKey to ExpressibleByStringLiteral. That way your
framework's users can still use headers[.lastModified] and
headers["X-MyHeader"] syntax, and it makes your library's code only very
slightly more complicated.

Not trying to tell you how to write your library, but this approach worked
really well for me recently.

Best,

Will

···

On Fri, Jun 9, 2017 at 12:14 AM Yvo van Beek via swift-evolution < swift-evolution@swift.org> wrote:

Typealiases can greatly reduce the complexity of code. But I think one
change in how the compiler handles them could make them even more powerful.

Let's say I'm creating a web server framework and I've created a simple
dictionary to store HTTP headers (I know that headers are more complex than
that, but as an example). I could write something like this:

    typealias HeaderKey = String

  var headers = [HeaderKey: String]()
  headers["Host"] = "domain.com"

Now I can define a couple of default headers like this:

  extension HeaderKey {
    static var lastModified: String { return "Last-Modified" }
    static var host: String { return "Host" }
  }

After that I can do this:

  var headers = [HeaderKey: String]()
  headers[.host] = "domain.com"
  headers[.lastModified] = "some date"
  headers["X-MyHeader"] = "This still works too"

But unfortunately the extension is also applied to normal strings:

    var normalString: String = .host

Perhaps it would be better if the extension would only apply to the parts
of my code where I use the HeaderKey typealias and not to all Strings. This
could be a great tool to specialize classes by creating a typealias and
adding functionality to it. Another example I can think of is typealiases
for dictionaries or arrays with added business logic through extensions
(especially since you can't inherit from structs).

If you want to create an extension that adds functionality to all Strings
you could have created an extension for String instead of HeaderKey.

Please let me know what you think. I'm not sure how complex this change
would be.
I could write a proposal if you're interested.

Kind regards,
Yvo
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Daryle Walker) #4

Typealiases can greatly reduce the complexity of code. But I think one change in how the compiler handles them could make them even more powerful.

[SNIP]

Perhaps it would be better if the extension would only apply to the parts of my code where I use the HeaderKey typealias and not to all Strings. This could be a great tool to specialize classes by creating a typealias and adding functionality to it. Another example I can think of is typealiases for dictionaries or arrays with added business logic through extensions (especially since you can't inherit from structs).

If you want to create an extension that adds functionality to all Strings you could have created an extension for String instead of HeaderKey.

Please let me know what you think. I'm not sure how complex this change would be.
I could write a proposal if you're interested.

Isn’t the point of “typealias" is that it does NOT have any change in semantics? The compiler doesn’t even have to acknowledge aliases in any run-time type tables, it just references the existing row of what the alias points to (based on a compile-time type table).

As others suggested, this new semantic could be moved to a new type concept (with a new keyword).

···

On Jun 9, 2017, at 12:14 AM, Yvo van Beek via swift-evolution <swift-evolution@swift.org> wrote:


Daryle Walker
Mac, Internet, and Video Game Junkie
darylew AT mac DOT com


(Yvo van Beek) #5

@Doug:

It might indeed be confusing to use "typealias" for this.

Looking at the HeaderKey example, alternatives could be:
1) struct inheritance (HeaderKey would inherit from String), value
subtyping?
2) the ability to specify implicit conversion between types, C# has
the implicit
keyword
<https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/implicit>
for this

@Will:

Your solution of creating a HeaderKey struct resolves most issues, but has
a few downsides:
1) the HeaderKey struct would contain quite some boilerplate code that
mimics String (Equality, CustomStringConvertible etc.)
2) the ExpressibleByStringLiteral enables the use of static strings, but a
dynamic string would need to be wrapped: HeaderKey(dynamicHeader) leading
to clunky code
3) the headers dictionary would become more difficult to process, the key
would no longer be an actual String, resulting in more code that distracts
from the actual code

Thanks everyone for chiming in on this.

···

On Fri, Jun 9, 2017 at 11:41 AM, Will Field-Thompson <will.a.ft@gmail.com> wrote:

I feel like this might be better served by something like newtype which I
know I've seen floating around on this list before. The idea is that
something like:

newtype HeaderKey = String

would serve roughly as syntactic sugar for something like

struct HeaderKey {
   let value: String
   init(_ value: String) { self.value = value }
   /* Possibly insert methods from String here, possibly do something
else, whatever */
}

I've always thought this might be interesting, but there's a few reasons I
suggest it in place of extending a typealias:
1) Backwards compatibility — this could very easily break existing code
2) Semantically it seems to me that an *alias* is a useful distinction
from *"let's make a new type" *— sometimes you actually just want to call
one type by another name in some context

Regardless, I think you could solve your particular problem like this:

public struct HeaderKey {
   let value: String
   public init(_ value: String) { self.value = value }
}

and conform HeaderKey to ExpressibleByStringLiteral. That way your
framework's users can still use headers[.lastModified] and
headers["X-MyHeader"] syntax, and it makes your library's code only very
slightly more complicated.

Not trying to tell you how to write your library, but this approach worked
really well for me recently.

Best,

Will

On Fri, Jun 9, 2017 at 12:14 AM Yvo van Beek via swift-evolution < > swift-evolution@swift.org> wrote:

Typealiases can greatly reduce the complexity of code. But I think one
change in how the compiler handles them could make them even more powerful.

Let's say I'm creating a web server framework and I've created a simple
dictionary to store HTTP headers (I know that headers are more complex than
that, but as an example). I could write something like this:

    typealias HeaderKey = String

  var headers = [HeaderKey: String]()
  headers["Host"] = "domain.com"

Now I can define a couple of default headers like this:

  extension HeaderKey {
    static var lastModified: String { return "Last-Modified" }
    static var host: String { return "Host" }
  }

After that I can do this:

  var headers = [HeaderKey: String]()
  headers[.host] = "domain.com"
  headers[.lastModified] = "some date"
  headers["X-MyHeader"] = "This still works too"

But unfortunately the extension is also applied to normal strings:

    var normalString: String = .host

Perhaps it would be better if the extension would only apply to the parts
of my code where I use the HeaderKey typealias and not to all Strings. This
could be a great tool to specialize classes by creating a typealias and
adding functionality to it. Another example I can think of is typealiases
for dictionaries or arrays with added business logic through extensions
(especially since you can't inherit from structs).

If you want to create an extension that adds functionality to all Strings
you could have created an extension for String instead of HeaderKey.

Please let me know what you think. I'm not sure how complex this change
would be.
I could write a proposal if you're interested.

Kind regards,
Yvo
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(David Sweeris) #6

Agreed... +1 for something like "newtype", -1 for hoisting that functionality onto "typealias".

- Dave Sweeris

···

On Jun 9, 2017, at 16:38, Daryle Walker via swift-evolution <swift-evolution@swift.org> wrote:

On Jun 9, 2017, at 12:14 AM, Yvo van Beek via swift-evolution <swift-evolution@swift.org> wrote:

Typealiases can greatly reduce the complexity of code. But I think one change in how the compiler handles them could make them even more powerful.

[SNIP]

Perhaps it would be better if the extension would only apply to the parts of my code where I use the HeaderKey typealias and not to all Strings. This could be a great tool to specialize classes by creating a typealias and adding functionality to it. Another example I can think of is typealiases for dictionaries or arrays with added business logic through extensions (especially since you can't inherit from structs).

If you want to create an extension that adds functionality to all Strings you could have created an extension for String instead of HeaderKey.

Please let me know what you think. I'm not sure how complex this change would be.
I could write a proposal if you're interested.

Isn’t the point of “typealias" is that it does NOT have any change in semantics? The compiler doesn’t even have to acknowledge aliases in any run-time type tables, it just references the existing row of what the alias points to (based on a compile-time type table).

As others suggested, this new semantic could be moved to a new type concept (with a new keyword).


(Charlie Monroe) #7

-1 - this would disallow e.g. to share UI code between iOS and macOS:

#if os(iOS)
  typealias XUView = UIView
#else
  typealias XUView = NSView
#endif

extension XUView {
  ...
}

or with any similar compatibility typealiases.

···

On Jun 9, 2017, at 5:38 PM, Jacob Williams via swift-evolution <swift-evolution@swift.org> wrote:

+1 from me.

There have been times I’ve wanted to subclass an object (such as String) but since it is a non-class, non-protocol type you can only extend Strings existing functionality which adds that same functionality to Strings everywhere. It would be nice if we could either extend type aliases (and only the type alias), or if it were possible to inherit from structs so that we could create a custom string type like so:

struct HeaderKey: String {
  static var lastModified: String { return “Last-Modified” }
  static var host: String { return “Host” }
}

I realize that struct inheritance is far less likely, since that defeats one of the main pieces of what makes a struct a struct. So I’m all for this proposal of allowing type aliases to be extended as though they were their own struct/class.

Unfortunately, I’m not sure how feasible this kind of functionality would actually be, but if it’s possible then I’m in favor of implementing it.

On Jun 8, 2017, at 10:14 PM, Yvo van Beek via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Typealiases can greatly reduce the complexity of code. But I think one change in how the compiler handles them could make them even more powerful.

Let's say I'm creating a web server framework and I've created a simple dictionary to store HTTP headers (I know that headers are more complex than that, but as an example). I could write something like this:

    typealias HeaderKey = String

  var headers = [HeaderKey: String]()
  headers["Host"] = "domain.com <http://domain.com/>"

Now I can define a couple of default headers like this:

  extension HeaderKey {
    static var lastModified: String { return "Last-Modified" }
    static var host: String { return "Host" }
  }

After that I can do this:

  var headers = [HeaderKey: String]()
  headers[.host] = "domain.com <http://domain.com/>"
  headers[.lastModified] = "some date"
  headers["X-MyHeader"] = "This still works too"

But unfortunately the extension is also applied to normal strings:

    var normalString: String = .host

Perhaps it would be better if the extension would only apply to the parts of my code where I use the HeaderKey typealias and not to all Strings. This could be a great tool to specialize classes by creating a typealias and adding functionality to it. Another example I can think of is typealiases for dictionaries or arrays with added business logic through extensions (especially since you can't inherit from structs).

If you want to create an extension that adds functionality to all Strings you could have created an extension for String instead of HeaderKey.

Please let me know what you think. I'm not sure how complex this change would be.
I could write a proposal if you're interested.

Kind regards,
Yvo
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto: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


(Gor Gyolchanyan) #8

This sounds awesome to me, except I'd take it a bit further and bring this idea to its logical conclusion: creating concrete type subtypes.
Take, for example the CGFloat, which is a struct, that's essentially a subtype of Double.
Seems like what you actually want is to create a subtype of String, while leaving String completely intact.
You could have defined a new type HeaderKey, have a single String member in it and define call forwarding functions that you need, which would solve your use case perfectly.
The only downside is the horrible amount of boilerplate you'd have to write, which is where some syntactic sugar could be introduced.

indirect struct HeaderKey {

  var value: String // Indirect structs must have exactly one stored variable named `value`.

}

extension HeaderKey: CustomStringInitializable {

  /// Implementing conformance is optional if `self.value` conforms to it.

}

···

On Jun 9, 2017, at 6:37 PM, Remy Demarest via swift-evolution <swift-evolution@swift.org> wrote:

+1 from me.

There have been times I’ve wanted to subclass an object (such as String) but since it is a non-class, non-protocol type you can only extend Strings existing functionality which adds that same functionality to Strings everywhere. It would be nice if we could either extend type aliases (and only the type alias), or if it were possible to inherit from structs so that we could create a custom string type like so:

struct HeaderKey: String {
  static var lastModified: String { return “Last-Modified” }
  static var host: String { return “Host” }
}

I realize that struct inheritance is far less likely, since that defeats one of the main pieces of what makes a struct a struct. So I’m all for this proposal of allowing type aliases to be extended as though they were their own struct/class.

Unfortunately, I’m not sure how feasible this kind of functionality would actually be, but if it’s possible then I’m in favor of implementing it.

On Jun 8, 2017, at 10:14 PM, Yvo van Beek via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Typealiases can greatly reduce the complexity of code. But I think one change in how the compiler handles them could make them even more powerful.

Let's say I'm creating a web server framework and I've created a simple dictionary to store HTTP headers (I know that headers are more complex than that, but as an example). I could write something like this:

    typealias HeaderKey = String

  var headers = [HeaderKey: String]()
  headers["Host"] = "domain.com <http://domain.com/>"

Now I can define a couple of default headers like this:

  extension HeaderKey {
    static var lastModified: String { return "Last-Modified" }
    static var host: String { return "Host" }
  }

After that I can do this:

  var headers = [HeaderKey: String]()
  headers[.host] = "domain.com <http://domain.com/>"
  headers[.lastModified] = "some date"
  headers["X-MyHeader"] = "This still works too"

But unfortunately the extension is also applied to normal strings:

    var normalString: String = .host

Perhaps it would be better if the extension would only apply to the parts of my code where I use the HeaderKey typealias and not to all Strings. This could be a great tool to specialize classes by creating a typealias and adding functionality to it. Another example I can think of is typealiases for dictionaries or arrays with added business logic through extensions (especially since you can't inherit from structs).

If you want to create an extension that adds functionality to all Strings you could have created an extension for String instead of HeaderKey.

Please let me know what you think. I'm not sure how complex this change would be.
I could write a proposal if you're interested.

Kind regards,
Yvo
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto: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


(Jacob Williams) #9

-1 - this would disallow e.g. to share UI code between iOS and macOS:

#if os(iOS)
  typealias XUView = UIView
#else
  typealias XUView = NSView
#endif

extension XUView {
  ...
}

I really don’t see how this disallows code sharing between the two systems? Could you explain further? Based on my understanding of the pitch, this is valid code still. (Although I do like the suggestion of a new keyword rather than just limiting type alias).

Even if your example was invalid, you could also just do something like this:

#if os(iOS)
  typealias XUView = UIView
  extension XUView {
    //extension code here
  }
#if os(macOS)
  typealias XUView = UIView
  extension XUView {
    // extension code here
  }
#endif

While not as pretty, still just as effective if you have to deal with different types based on the system being compiled for and you could easily still make the type alias extensions for each type work the same.

···

On Jun 9, 2017, at 9:53 AM, Charlie Monroe <charlie@charliemonroe.net> wrote:

On Jun 9, 2017, at 9:53 AM, Charlie Monroe <charlie@charliemonroe.net> wrote:

-1 - this would disallow e.g. to share UI code between iOS and macOS:

#if os(iOS)
  typealias XUView = UIView
#else
  typealias XUView = NSView
#endif

extension XUView {
  ...
}

or with any similar compatibility typealiases.

On Jun 9, 2017, at 5:38 PM, Jacob Williams via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

+1 from me.

There have been times I’ve wanted to subclass an object (such as String) but since it is a non-class, non-protocol type you can only extend Strings existing functionality which adds that same functionality to Strings everywhere. It would be nice if we could either extend type aliases (and only the type alias), or if it were possible to inherit from structs so that we could create a custom string type like so:

struct HeaderKey: String {
  static var lastModified: String { return “Last-Modified” }
  static var host: String { return “Host” }
}

I realize that struct inheritance is far less likely, since that defeats one of the main pieces of what makes a struct a struct. So I’m all for this proposal of allowing type aliases to be extended as though they were their own struct/class.

Unfortunately, I’m not sure how feasible this kind of functionality would actually be, but if it’s possible then I’m in favor of implementing it.

On Jun 8, 2017, at 10:14 PM, Yvo van Beek via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Typealiases can greatly reduce the complexity of code. But I think one change in how the compiler handles them could make them even more powerful.

Let's say I'm creating a web server framework and I've created a simple dictionary to store HTTP headers (I know that headers are more complex than that, but as an example). I could write something like this:

    typealias HeaderKey = String

  var headers = [HeaderKey: String]()
  headers["Host"] = "domain.com <http://domain.com/>"

Now I can define a couple of default headers like this:

  extension HeaderKey {
    static var lastModified: String { return "Last-Modified" }
    static var host: String { return "Host" }
  }

After that I can do this:

  var headers = [HeaderKey: String]()
  headers[.host] = "domain.com <http://domain.com/>"
  headers[.lastModified] = "some date"
  headers["X-MyHeader"] = "This still works too"

But unfortunately the extension is also applied to normal strings:

    var normalString: String = .host

Perhaps it would be better if the extension would only apply to the parts of my code where I use the HeaderKey typealias and not to all Strings. This could be a great tool to specialize classes by creating a typealias and adding functionality to it. Another example I can think of is typealiases for dictionaries or arrays with added business logic through extensions (especially since you can't inherit from structs).

If you want to create an extension that adds functionality to all Strings you could have created an extension for String instead of HeaderKey.

Please let me know what you think. I'm not sure how complex this change would be.
I could write a proposal if you're interested.

Kind regards,
Yvo
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

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


#10

Somewhat related to this, shouldn’t it be possible to sub-struct a struct as long as you only add functions and computed properties (i.e., no stored properties)? Traditionally structs cannot be subtyped because their size must be known at compile time. I don’t know the implementation details of where functions and computed properties live, but something tells me they belong to the type and not the object (although I’ve never really made the effort to sit down and fully understand Swift’s type model), in which case adding them to a struct’s definition would not change the size of the object on the stack. Thus it should be possible to make custom substructs of String that add additional functionality but no new stored properties. Thoughts?

···

On Jun 9, 2017, at 12:12 PM, Jacob Williams via swift-evolution <swift-evolution@swift.org> wrote:

On Jun 9, 2017, at 9:53 AM, Charlie Monroe <charlie@charliemonroe.net> wrote:

-1 - this would disallow e.g. to share UI code between iOS and macOS:

#if os(iOS)
  typealias XUView = UIView
#else
  typealias XUView = NSView
#endif

extension XUView {
  ...
}

I really don’t see how this disallows code sharing between the two systems? Could you explain further? Based on my understanding of the pitch, this is valid code still. (Although I do like the suggestion of a new keyword rather than just limiting type alias).

Even if your example was invalid, you could also just do something like this:

#if os(iOS)
  typealias XUView = UIView
  extension XUView {
    //extension code here
  }
#if os(macOS)
  typealias XUView = UIView
  extension XUView {
    // extension code here
  }
#endif

While not as pretty, still just as effective if you have to deal with different types based on the system being compiled for and you could easily still make the type alias extensions for each type work the same.

On Jun 9, 2017, at 9:53 AM, Charlie Monroe <charlie@charliemonroe.net> wrote:

-1 - this would disallow e.g. to share UI code between iOS and macOS:

#if os(iOS)
  typealias XUView = UIView
#else
  typealias XUView = NSView
#endif

extension XUView {
  ...
}

or with any similar compatibility typealiases.

On Jun 9, 2017, at 5:38 PM, Jacob Williams via swift-evolution <swift-evolution@swift.org> wrote:

+1 from me.

There have been times I’ve wanted to subclass an object (such as String) but since it is a non-class, non-protocol type you can only extend Strings existing functionality which adds that same functionality to Strings everywhere. It would be nice if we could either extend type aliases (and only the type alias), or if it were possible to inherit from structs so that we could create a custom string type like so:

struct HeaderKey: String {
  static var lastModified: String { return “Last-Modified” }
  static var host: String { return “Host” }
}

I realize that struct inheritance is far less likely, since that defeats one of the main pieces of what makes a struct a struct. So I’m all for this proposal of allowing type aliases to be extended as though they were their own struct/class.

Unfortunately, I’m not sure how feasible this kind of functionality would actually be, but if it’s possible then I’m in favor of implementing it.

On Jun 8, 2017, at 10:14 PM, Yvo van Beek via swift-evolution <swift-evolution@swift.org> wrote:

Typealiases can greatly reduce the complexity of code. But I think one change in how the compiler handles them could make them even more powerful.

Let's say I'm creating a web server framework and I've created a simple dictionary to store HTTP headers (I know that headers are more complex than that, but as an example). I could write something like this:

    typealias HeaderKey = String

  var headers = [HeaderKey: String]()
  headers["Host"] = "domain.com"

Now I can define a couple of default headers like this:

  extension HeaderKey {
    static var lastModified: String { return "Last-Modified" }
    static var host: String { return "Host" }
  }

After that I can do this:

  var headers = [HeaderKey: String]()
  headers[.host] = "domain.com"
  headers[.lastModified] = "some date"
  headers["X-MyHeader"] = "This still works too"

But unfortunately the extension is also applied to normal strings:

    var normalString: String = .host

Perhaps it would be better if the extension would only apply to the parts of my code where I use the HeaderKey typealias and not to all Strings. This could be a great tool to specialize classes by creating a typealias and adding functionality to it. Another example I can think of is typealiases for dictionaries or arrays with added business logic through extensions (especially since you can't inherit from structs).

If you want to create an extension that adds functionality to all Strings you could have created an extension for String instead of HeaderKey.

Please let me know what you think. I'm not sure how complex this change would be.
I could write a proposal if you're interested.

Kind regards,
Yvo
_______________________________________________
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

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


(Matthew Johnson) #11

Somewhat related to this, shouldn’t it be possible to sub-struct a struct as long as you only add functions and computed properties (i.e., no stored properties)? Traditionally structs cannot be subtyped because their size must be known at compile time. I don’t know the implementation details of where functions and computed properties live, but something tells me they belong to the type and not the object (although I’ve never really made the effort to sit down and fully understand Swift’s type model), in which case adding them to a struct’s definition would not change the size of the object on the stack. Thus it should be possible to make custom substructs of String that add additional functionality but no new stored properties. Thoughts?

Value subtyping is a large subject and, IIUC, newtype would be a subset of that topic. Unlikely to be in scope for Swift 5, though, but that’s up to the core team.

I see newtype as being more related to forwarding than subtyping. Usually you want to hide significant parts of the interface to the wrapped type.

···

On Jun 9, 2017, at 12:09 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:
On Fri, Jun 9, 2017 at 12:44 Robert Bennett via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jun 9, 2017, at 12:12 PM, Jacob Williams via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jun 9, 2017, at 9:53 AM, Charlie Monroe <charlie@charliemonroe.net <mailto:charlie@charliemonroe.net>> wrote:

-1 - this would disallow e.g. to share UI code between iOS and macOS:

#if os(iOS)
  typealias XUView = UIView
#else
  typealias XUView = NSView
#endif

extension XUView {
  ...
}

I really don’t see how this disallows code sharing between the two systems? Could you explain further? Based on my understanding of the pitch, this is valid code still. (Although I do like the suggestion of a new keyword rather than just limiting type alias).

Even if your example was invalid, you could also just do something like this:

#if os(iOS)
  typealias XUView = UIView
  extension XUView {
    //extension code here
  }
#if os(macOS)
  typealias XUView = UIView
  extension XUView {
    // extension code here
  }
#endif

While not as pretty, still just as effective if you have to deal with different types based on the system being compiled for and you could easily still make the type alias extensions for each type work the same.

On Jun 9, 2017, at 9:53 AM, Charlie Monroe <charlie@charliemonroe.net <mailto:charlie@charliemonroe.net>> wrote:

-1 - this would disallow e.g. to share UI code between iOS and macOS:

#if os(iOS)
  typealias XUView = UIView
#else
  typealias XUView = NSView
#endif

extension XUView {
  ...
}

or with any similar compatibility typealiases.

On Jun 9, 2017, at 5:38 PM, Jacob Williams via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

+1 from me.

There have been times I’ve wanted to subclass an object (such as String) but since it is a non-class, non-protocol type you can only extend Strings existing functionality which adds that same functionality to Strings everywhere. It would be nice if we could either extend type aliases (and only the type alias), or if it were possible to inherit from structs so that we could create a custom string type like so:

struct HeaderKey: String {
  static var lastModified: String { return “Last-Modified” }
  static var host: String { return “Host” }
}

I realize that struct inheritance is far less likely, since that defeats one of the main pieces of what makes a struct a struct. So I’m all for this proposal of allowing type aliases to be extended as though they were their own struct/class.

Unfortunately, I’m not sure how feasible this kind of functionality would actually be, but if it’s possible then I’m in favor of implementing it.

On Jun 8, 2017, at 10:14 PM, Yvo van Beek via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Typealiases can greatly reduce the complexity of code. But I think one change in how the compiler handles them could make them even more powerful.

Let's say I'm creating a web server framework and I've created a simple dictionary to store HTTP headers (I know that headers are more complex than that, but as an example). I could write something like this:

    typealias HeaderKey = String

  var headers = [HeaderKey: String]()
  headers["Host"] = "domain.com <http://domain.com/>"

Now I can define a couple of default headers like this:

  extension HeaderKey {
    static var lastModified: String { return "Last-Modified" }
    static var host: String { return "Host" }
  }

After that I can do this:

  var headers = [HeaderKey: String]()
  headers[.host] = "domain.com <http://domain.com/>"
  headers[.lastModified] = "some date"
  headers["X-MyHeader"] = "This still works too"

But unfortunately the extension is also applied to normal strings:

    var normalString: String = .host

Perhaps it would be better if the extension would only apply to the parts of my code where I use the HeaderKey typealias and not to all Strings. This could be a great tool to specialize classes by creating a typealias and adding functionality to it. Another example I can think of is typealiases for dictionaries or arrays with added business logic through extensions (especially since you can't inherit from structs).

If you want to create an extension that adds functionality to all Strings you could have created an extension for String instead of HeaderKey.

Please let me know what you think. I'm not sure how complex this change would be.
I could write a proposal if you're interested.

Kind regards,
Yvo
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

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

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

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto: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


(Xiaodi Wu) #12

Somewhat related to this, shouldn’t it be possible to sub-struct a struct
as long as you only add functions and computed properties (i.e., no stored
properties)? Traditionally structs cannot be subtyped because their size
must be known at compile time. I don’t know the implementation details of
where functions and computed properties live, but something tells me they
belong to the type and not the object (although I’ve never really made the
effort to sit down and fully understand Swift’s type model), in which case
adding them to a struct’s definition would not change the size of the
object on the stack. Thus it should be possible to make custom substructs
of String that add additional functionality but no new stored properties.
Thoughts?

Value subtyping is a large subject and, IIUC, newtype would be a subset of
that topic. Unlikely to be in scope for Swift 5, though, but that’s up to
the core team.

···

On Fri, Jun 9, 2017 at 12:44 Robert Bennett via swift-evolution < swift-evolution@swift.org> wrote:

On Jun 9, 2017, at 12:12 PM, Jacob Williams via swift-evolution < > swift-evolution@swift.org> wrote:

On Jun 9, 2017, at 9:53 AM, Charlie Monroe <charlie@charliemonroe.net> > wrote:

-1 - this would disallow e.g. to share UI code between iOS and macOS:

#if os(iOS)
typealias XUView = UIView
#else
typealias XUView = NSView
#endif

extension XUView {
...
}

I really don’t see how this disallows code sharing between the two
systems? Could you explain further? Based on my understanding of the pitch,
this is valid code still. (Although I do like the suggestion of a new
keyword rather than just limiting type alias).

Even if your example was invalid, you could also just do something like
this:

#if os(iOS)
typealias XUView = UIView
extension XUView {
//extension code here
}
#if os(macOS)
typealias XUView = UIView
extension XUView {
// extension code here
}
#endif

While not as pretty, still just as effective if you have to deal with
different types based on the system being compiled for and you could easily
still make the type alias extensions for each type work the same.

On Jun 9, 2017, at 9:53 AM, Charlie Monroe <charlie@charliemonroe.net> > wrote:

-1 - this would disallow e.g. to share UI code between iOS and macOS:

#if os(iOS)
typealias XUView = UIView
#else
typealias XUView = NSView
#endif

extension XUView {
...
}

or with any similar compatibility typealiases.

On Jun 9, 2017, at 5:38 PM, Jacob Williams via swift-evolution < > swift-evolution@swift.org> wrote:

+1 from me.

There have been times I’ve wanted to subclass an object (such as String)
but since it is a non-class, non-protocol type you can only extend Strings
existing functionality which adds that same functionality to Strings
everywhere. It would be nice if we could either extend type aliases (and
only the type alias), or if it were possible to inherit from structs so
that we could create a custom string type like so:

struct HeaderKey: String {
static var lastModified: String { return “Last-Modified” }
static var host: String { return “Host” }
}

I realize that struct inheritance is far less likely, since that defeats
one of the main pieces of what makes a struct a struct. So I’m all for this
proposal of allowing type aliases to be extended as though they were their
own struct/class.

Unfortunately, I’m not sure how feasible this kind of functionality would
actually be, but if it’s possible then I’m in favor of implementing it.

On Jun 8, 2017, at 10:14 PM, Yvo van Beek via swift-evolution < > swift-evolution@swift.org> wrote:

Typealiases can greatly reduce the complexity of code. But I think one
change in how the compiler handles them could make them even more powerful.

Let's say I'm creating a web server framework and I've created a simple
dictionary to store HTTP headers (I know that headers are more complex than
that, but as an example). I could write something like this:

    typealias HeaderKey = String

  var headers = [HeaderKey: String]()
  headers["Host"] = "domain.com"

Now I can define a couple of default headers like this:

  extension HeaderKey {
    static var lastModified: String { return "Last-Modified" }
    static var host: String { return "Host" }
  }

After that I can do this:

  var headers = [HeaderKey: String]()
  headers[.host] = "domain.com"
  headers[.lastModified] = "some date"
  headers["X-MyHeader"] = "This still works too"

But unfortunately the extension is also applied to normal strings:

    var normalString: String = .host

Perhaps it would be better if the extension would only apply to the parts
of my code where I use the HeaderKey typealias and not to all Strings. This
could be a great tool to specialize classes by creating a typealias and
adding functionality to it. Another example I can think of is typealiases
for dictionaries or arrays with added business logic through extensions
(especially since you can't inherit from structs).

If you want to create an extension that adds functionality to all Strings
you could have created an extension for String instead of HeaderKey.

Please let me know what you think. I'm not sure how complex this change
would be.
I could write a proposal if you're interested.

Kind regards,
Yvo
_______________________________________________
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

_______________________________________________
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


(Charlie Monroe) #13

-1 - this would disallow e.g. to share UI code between iOS and macOS:

#if os(iOS)
  typealias XUView = UIView
#else
  typealias XUView = NSView
#endif

extension XUView {
  ...
}

I really don’t see how this disallows code sharing between the two systems? Could you explain further? Based on my understanding of the pitch, this is valid code still. (Although I do like the suggestion of a new keyword rather than just limiting type alias).

Even if your example was invalid, you could also just do something like this:

#if os(iOS)
  typealias XUView = UIView
  extension XUView {
    //extension code here
  }
#if os(macOS)
  typealias XUView = UIView
  extension XUView {
    // extension code here
  }
#endif

Um... And you'd implement the same thing twice? The idea behind declaring the typealias and extension on it is that you can easily create additional functionality with the same code base on both UIView and NSView without duplicit code.

From what I understand that is suggested by this proposal, given the following example expanding my original:

/// Extension of NSView/UIView depending on the target.
extension XUView {

  /// Creates some subview and returns it.
  func createSubview() -> XUView {
    ...
  }

}

I wouldn't then be able to:

But unfortunately the extension is also applied to normal strings:

    var normalString: String = .host

a) assign: let view: UIView = view.createSubview()

Perhaps it would be better if the extension would only apply to the parts of my code where I use the HeaderKey typealias and not to all Strings.

b) use any of the extensions of views that are not declared as the typealias "XUView" (e.g. anything in the Cocoa/UIKit frameworks).

···

On Jun 9, 2017, at 6:12 PM, Jacob Williams <ponyboy47@gmail.com> wrote:

On Jun 9, 2017, at 9:53 AM, Charlie Monroe <charlie@charliemonroe.net <mailto:charlie@charliemonroe.net>> wrote:

While not as pretty, still just as effective if you have to deal with different types based on the system being compiled for and you could easily still make the type alias extensions for each type work the same.

On Jun 9, 2017, at 9:53 AM, Charlie Monroe <charlie@charliemonroe.net <mailto:charlie@charliemonroe.net>> wrote:

-1 - this would disallow e.g. to share UI code between iOS and macOS:

#if os(iOS)
  typealias XUView = UIView
#else
  typealias XUView = NSView
#endif

extension XUView {
  ...
}

or with any similar compatibility typealiases.

On Jun 9, 2017, at 5:38 PM, Jacob Williams via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

+1 from me.

There have been times I’ve wanted to subclass an object (such as String) but since it is a non-class, non-protocol type you can only extend Strings existing functionality which adds that same functionality to Strings everywhere. It would be nice if we could either extend type aliases (and only the type alias), or if it were possible to inherit from structs so that we could create a custom string type like so:

struct HeaderKey: String {
  static var lastModified: String { return “Last-Modified” }
  static var host: String { return “Host” }
}

I realize that struct inheritance is far less likely, since that defeats one of the main pieces of what makes a struct a struct. So I’m all for this proposal of allowing type aliases to be extended as though they were their own struct/class.

Unfortunately, I’m not sure how feasible this kind of functionality would actually be, but if it’s possible then I’m in favor of implementing it.

On Jun 8, 2017, at 10:14 PM, Yvo van Beek via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Typealiases can greatly reduce the complexity of code. But I think one change in how the compiler handles them could make them even more powerful.

Let's say I'm creating a web server framework and I've created a simple dictionary to store HTTP headers (I know that headers are more complex than that, but as an example). I could write something like this:

    typealias HeaderKey = String

  var headers = [HeaderKey: String]()
  headers["Host"] = "domain.com <http://domain.com/>"

Now I can define a couple of default headers like this:

  extension HeaderKey {
    static var lastModified: String { return "Last-Modified" }
    static var host: String { return "Host" }
  }

After that I can do this:

  var headers = [HeaderKey: String]()
  headers[.host] = "domain.com <http://domain.com/>"
  headers[.lastModified] = "some date"
  headers["X-MyHeader"] = "This still works too"

But unfortunately the extension is also applied to normal strings:

    var normalString: String = .host

Perhaps it would be better if the extension would only apply to the parts of my code where I use the HeaderKey typealias and not to all Strings. This could be a great tool to specialize classes by creating a typealias and adding functionality to it. Another example I can think of is typealiases for dictionaries or arrays with added business logic through extensions (especially since you can't inherit from structs).

If you want to create an extension that adds functionality to all Strings you could have created an extension for String instead of HeaderKey.

Please let me know what you think. I'm not sure how complex this change would be.
I could write a proposal if you're interested.

Kind regards,
Yvo
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

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


(Xiaodi Wu) #14

Interesting. So you’d want `newtype Foo = String` to start off with no
members on Foo?

···

On Fri, Jun 9, 2017 at 15:18 Matthew Johnson <matthew@anandabits.com> wrote:

On Jun 9, 2017, at 12:09 PM, Xiaodi Wu via swift-evolution < > swift-evolution@swift.org> wrote:

On Fri, Jun 9, 2017 at 12:44 Robert Bennett via swift-evolution < > swift-evolution@swift.org> wrote:

Somewhat related to this, shouldn’t it be possible to sub-struct a struct
as long as you only add functions and computed properties (i.e., no stored
properties)? Traditionally structs cannot be subtyped because their size
must be known at compile time. I don’t know the implementation details of
where functions and computed properties live, but something tells me they
belong to the type and not the object (although I’ve never really made the
effort to sit down and fully understand Swift’s type model), in which case
adding them to a struct’s definition would not change the size of the
object on the stack. Thus it should be possible to make custom substructs
of String that add additional functionality but no new stored properties.
Thoughts?

Value subtyping is a large subject and, IIUC, newtype would be a subset of
that topic. Unlikely to be in scope for Swift 5, though, but that’s up to
the core team.

I see newtype as being more related to forwarding than subtyping. Usually
you want to hide significant parts of the interface to the wrapped type.

On Jun 9, 2017, at 12:12 PM, Jacob Williams via swift-evolution < >> swift-evolution@swift.org> wrote:

On Jun 9, 2017, at 9:53 AM, Charlie Monroe <charlie@charliemonroe.net> >> wrote:

-1 - this would disallow e.g. to share UI code between iOS and macOS:

#if os(iOS)
typealias XUView = UIView
#else
typealias XUView = NSView
#endif

extension XUView {
...
}

I really don’t see how this disallows code sharing between the two
systems? Could you explain further? Based on my understanding of the pitch,
this is valid code still. (Although I do like the suggestion of a new
keyword rather than just limiting type alias).

Even if your example was invalid, you could also just do something like
this:

#if os(iOS)
typealias XUView = UIView
extension XUView {
//extension code here
}
#if os(macOS)
typealias XUView = UIView
extension XUView {
// extension code here
}
#endif

While not as pretty, still just as effective if you have to deal with
different types based on the system being compiled for and you could easily
still make the type alias extensions for each type work the same.

On Jun 9, 2017, at 9:53 AM, Charlie Monroe <charlie@charliemonroe.net> >> wrote:

-1 - this would disallow e.g. to share UI code between iOS and macOS:

#if os(iOS)
typealias XUView = UIView
#else
typealias XUView = NSView
#endif

extension XUView {
...
}

or with any similar compatibility typealiases.

On Jun 9, 2017, at 5:38 PM, Jacob Williams via swift-evolution < >> swift-evolution@swift.org> wrote:

+1 from me.

There have been times I’ve wanted to subclass an object (such as String)
but since it is a non-class, non-protocol type you can only extend Strings
existing functionality which adds that same functionality to Strings
everywhere. It would be nice if we could either extend type aliases (and
only the type alias), or if it were possible to inherit from structs so
that we could create a custom string type like so:

struct HeaderKey: String {
static var lastModified: String { return “Last-Modified” }
static var host: String { return “Host” }
}

I realize that struct inheritance is far less likely, since that defeats
one of the main pieces of what makes a struct a struct. So I’m all for this
proposal of allowing type aliases to be extended as though they were their
own struct/class.

Unfortunately, I’m not sure how feasible this kind of functionality would
actually be, but if it’s possible then I’m in favor of implementing it.

On Jun 8, 2017, at 10:14 PM, Yvo van Beek via swift-evolution < >> swift-evolution@swift.org> wrote:

Typealiases can greatly reduce the complexity of code. But I think one
change in how the compiler handles them could make them even more powerful.

Let's say I'm creating a web server framework and I've created a simple
dictionary to store HTTP headers (I know that headers are more complex than
that, but as an example). I could write something like this:

    typealias HeaderKey = String

  var headers = [HeaderKey: String]()
  headers["Host"] = "domain.com"

Now I can define a couple of default headers like this:

  extension HeaderKey {
    static var lastModified: String { return "Last-Modified" }
    static var host: String { return "Host" }
  }

After that I can do this:

  var headers = [HeaderKey: String]()
  headers[.host] = "domain.com"
  headers[.lastModified] = "some date"
  headers["X-MyHeader"] = "This still works too"

But unfortunately the extension is also applied to normal strings:

    var normalString: String = .host

Perhaps it would be better if the extension would only apply to the parts
of my code where I use the HeaderKey typealias and not to all Strings. This
could be a great tool to specialize classes by creating a typealias and
adding functionality to it. Another example I can think of is typealiases
for dictionaries or arrays with added business logic through extensions
(especially since you can't inherit from structs).

If you want to create an extension that adds functionality to all Strings
you could have created an extension for String instead of HeaderKey.

Please let me know what you think. I'm not sure how complex this change
would be.
I could write a proposal if you're interested.

Kind regards,
Yvo
_______________________________________________
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

_______________________________________________
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

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


(Dave Abrahams) #15

Yes, and whether you want an implicit conversion relationship between
the newtype and the old is an orthogonal issue.

···

on Fri Jun 09 2017, Matthew Johnson <swift-evolution@swift.org> wrote:

On Jun 9, 2017, at 12:09 PM, Xiaodi Wu via swift-evolution >> <swift-evolution@swift.org> wrote:

On Fri, Jun 9, 2017 at 12:44 Robert Bennett via swift-evolution >> <swift-evolution@swift.org > >> <mailto:swift-evolution@swift.org>> >> wrote:
Somewhat related to this, shouldn’t it be possible to sub-struct a
struct as long as you only add functions and computed properties
(i.e., no stored properties)? Traditionally structs cannot be
subtyped because their size must be known at compile time. I don’t
know the implementation details of where functions and computed
properties live, but something tells me they belong to the type and
not the object (although I’ve never really made the effort to sit
down and fully understand Swift’s type model), in which case adding
them to a struct’s definition would not change the size of the
object on the stack. Thus it should be possible to make custom
substructs of String that add additional functionality but no new
stored properties. Thoughts?

Value subtyping is a large subject and, IIUC, newtype would be a
subset of that topic. Unlikely to be in scope for Swift 5, though,
but that’s up to the core team.

I see newtype as being more related to forwarding than subtyping.
Usually you want to hide significant parts of the interface to the
wrapped type.

--
-Dave


(Matthew Johnson) #16

Interesting. So you’d want `newtype Foo = String` to start off with no members on Foo?

Yeah. Previous discussions of newtype have usually led to discussion of ways to forward using a protocol-oriented approach. Nothing has gotten too far, but it usually comes up that suppressing undesired members is important.

It is also important to have some way to distinguish between members with a parameter of the underlying type from members that should be treated by newtype as Self parameters. The mechanism we have for doing that in Swift happens to be a protocol.

···

On Jun 9, 2017, at 2:39 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Fri, Jun 9, 2017 at 15:18 Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> wrote:

On Jun 9, 2017, at 12:09 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Fri, Jun 9, 2017 at 12:44 Robert Bennett via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Somewhat related to this, shouldn’t it be possible to sub-struct a struct as long as you only add functions and computed properties (i.e., no stored properties)? Traditionally structs cannot be subtyped because their size must be known at compile time. I don’t know the implementation details of where functions and computed properties live, but something tells me they belong to the type and not the object (although I’ve never really made the effort to sit down and fully understand Swift’s type model), in which case adding them to a struct’s definition would not change the size of the object on the stack. Thus it should be possible to make custom substructs of String that add additional functionality but no new stored properties. Thoughts?

Value subtyping is a large subject and, IIUC, newtype would be a subset of that topic. Unlikely to be in scope for Swift 5, though, but that’s up to the core team.

I see newtype as being more related to forwarding than subtyping. Usually you want to hide significant parts of the interface to the wrapped type.

On Jun 9, 2017, at 12:12 PM, Jacob Williams via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jun 9, 2017, at 9:53 AM, Charlie Monroe <charlie@charliemonroe.net <mailto:charlie@charliemonroe.net>> wrote:

-1 - this would disallow e.g. to share UI code between iOS and macOS:

#if os(iOS)
  typealias XUView = UIView
#else
  typealias XUView = NSView
#endif

extension XUView {
  ...
}

I really don’t see how this disallows code sharing between the two systems? Could you explain further? Based on my understanding of the pitch, this is valid code still. (Although I do like the suggestion of a new keyword rather than just limiting type alias).

Even if your example was invalid, you could also just do something like this:

#if os(iOS)
  typealias XUView = UIView
  extension XUView {
    //extension code here
  }
#if os(macOS)
  typealias XUView = UIView
  extension XUView {
    // extension code here
  }
#endif

While not as pretty, still just as effective if you have to deal with different types based on the system being compiled for and you could easily still make the type alias extensions for each type work the same.

On Jun 9, 2017, at 9:53 AM, Charlie Monroe <charlie@charliemonroe.net <mailto:charlie@charliemonroe.net>> wrote:

-1 - this would disallow e.g. to share UI code between iOS and macOS:

#if os(iOS)
  typealias XUView = UIView
#else
  typealias XUView = NSView
#endif

extension XUView {
  ...
}

or with any similar compatibility typealiases.

On Jun 9, 2017, at 5:38 PM, Jacob Williams via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

+1 from me.

There have been times I’ve wanted to subclass an object (such as String) but since it is a non-class, non-protocol type you can only extend Strings existing functionality which adds that same functionality to Strings everywhere. It would be nice if we could either extend type aliases (and only the type alias), or if it were possible to inherit from structs so that we could create a custom string type like so:

struct HeaderKey: String {
  static var lastModified: String { return “Last-Modified” }
  static var host: String { return “Host” }
}

I realize that struct inheritance is far less likely, since that defeats one of the main pieces of what makes a struct a struct. So I’m all for this proposal of allowing type aliases to be extended as though they were their own struct/class.

Unfortunately, I’m not sure how feasible this kind of functionality would actually be, but if it’s possible then I’m in favor of implementing it.

On Jun 8, 2017, at 10:14 PM, Yvo van Beek via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Typealiases can greatly reduce the complexity of code. But I think one change in how the compiler handles them could make them even more powerful.

Let's say I'm creating a web server framework and I've created a simple dictionary to store HTTP headers (I know that headers are more complex than that, but as an example). I could write something like this:

    typealias HeaderKey = String

  var headers = [HeaderKey: String]()
  headers["Host"] = "domain.com <http://domain.com/>"

Now I can define a couple of default headers like this:

  extension HeaderKey {
    static var lastModified: String { return "Last-Modified" }
    static var host: String { return "Host" }
  }

After that I can do this:

  var headers = [HeaderKey: String]()
  headers[.host] = "domain.com <http://domain.com/>"
  headers[.lastModified] = "some date"
  headers["X-MyHeader"] = "This still works too"

But unfortunately the extension is also applied to normal strings:

    var normalString: String = .host

Perhaps it would be better if the extension would only apply to the parts of my code where I use the HeaderKey typealias and not to all Strings. This could be a great tool to specialize classes by creating a typealias and adding functionality to it. Another example I can think of is typealiases for dictionaries or arrays with added business logic through extensions (especially since you can't inherit from structs).

If you want to create an extension that adds functionality to all Strings you could have created an extension for String instead of HeaderKey.

Please let me know what you think. I'm not sure how complex this change would be.
I could write a proposal if you're interested.

Kind regards,
Yvo
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

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

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

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


(Tony Allevato) #17

+1 to this. My ideal imagining of this behavior is to:

1) Define a protocol containing the precise operations you want to support
via forwarding.
2) Internally/fileprivately extend the original type to conform to this
protocol.
3) In your new type, tell it to forward protocol members for a particular
property (the underlying String, or whatever) and the compiler will
synthesize the protocol member stubs on the new type.

There are definitely finer details to be worked out but that's a rough
sketch of how I've been imagining it could work. Nice advantages include
(1) if there already exists a protocol that defines the exact operations
you want to forward, you can skip step 2, and (2) it's explicit/opt-in
synthesis where you have to provide the members to forward (this is
expected, at a minimum), but you don't have to write all the boilerplate
implementations.

···

On Fri, Jun 9, 2017 at 12:47 PM Matthew Johnson via swift-evolution < swift-evolution@swift.org> wrote:

On Jun 9, 2017, at 2:39 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Interesting. So you’d want `newtype Foo = String` to start off with no
members on Foo?

Yeah. Previous discussions of newtype have usually led to discussion of
ways to forward using a protocol-oriented approach. Nothing has gotten too
far, but it usually comes up that suppressing undesired members is
important.

It is also important to have some way to distinguish between members with
a parameter of the underlying type from members that should be treated by
newtype as Self parameters. The mechanism we have for doing that in Swift
happens to be a protocol.

On Fri, Jun 9, 2017 at 15:18 Matthew Johnson <matthew@anandabits.com> > wrote:

On Jun 9, 2017, at 12:09 PM, Xiaodi Wu via swift-evolution < >> swift-evolution@swift.org> wrote:

On Fri, Jun 9, 2017 at 12:44 Robert Bennett via swift-evolution < >> swift-evolution@swift.org> wrote:

Somewhat related to this, shouldn’t it be possible to sub-struct a
struct as long as you only add functions and computed properties (i.e., no
stored properties)? Traditionally structs cannot be subtyped because their
size must be known at compile time. I don’t know the implementation details
of where functions and computed properties live, but something tells me
they belong to the type and not the object (although I’ve never really made
the effort to sit down and fully understand Swift’s type model), in which
case adding them to a struct’s definition would not change the size of the
object on the stack. Thus it should be possible to make custom substructs
of String that add additional functionality but no new stored properties.
Thoughts?

Value subtyping is a large subject and, IIUC, newtype would be a subset
of that topic. Unlikely to be in scope for Swift 5, though, but that’s up
to the core team.

I see newtype as being more related to forwarding than subtyping.
Usually you want to hide significant parts of the interface to the wrapped
type.

On Jun 9, 2017, at 12:12 PM, Jacob Williams via swift-evolution < >>> swift-evolution@swift.org> wrote:

On Jun 9, 2017, at 9:53 AM, Charlie Monroe <charlie@charliemonroe.net> >>> wrote:

-1 - this would disallow e.g. to share UI code between iOS and macOS:

#if os(iOS)
typealias XUView = UIView
#else
typealias XUView = NSView
#endif

extension XUView {
...
}

I really don’t see how this disallows code sharing between the two
systems? Could you explain further? Based on my understanding of the pitch,
this is valid code still. (Although I do like the suggestion of a new
keyword rather than just limiting type alias).

Even if your example was invalid, you could also just do something like
this:

#if os(iOS)
typealias XUView = UIView
extension XUView {
//extension code here
}
#if os(macOS)
typealias XUView = UIView
extension XUView {
// extension code here
}
#endif

While not as pretty, still just as effective if you have to deal with
different types based on the system being compiled for and you could easily
still make the type alias extensions for each type work the same.

On Jun 9, 2017, at 9:53 AM, Charlie Monroe <charlie@charliemonroe.net> >>> wrote:

-1 - this would disallow e.g. to share UI code between iOS and macOS:

#if os(iOS)
typealias XUView = UIView
#else
typealias XUView = NSView
#endif

extension XUView {
...
}

or with any similar compatibility typealiases.

On Jun 9, 2017, at 5:38 PM, Jacob Williams via swift-evolution < >>> swift-evolution@swift.org> wrote:

+1 from me.

There have been times I’ve wanted to subclass an object (such as String)
but since it is a non-class, non-protocol type you can only extend Strings
existing functionality which adds that same functionality to Strings
everywhere. It would be nice if we could either extend type aliases (and
only the type alias), or if it were possible to inherit from structs so
that we could create a custom string type like so:

struct HeaderKey: String {
static var lastModified: String { return “Last-Modified” }
static var host: String { return “Host” }
}

I realize that struct inheritance is far less likely, since that defeats
one of the main pieces of what makes a struct a struct. So I’m all for this
proposal of allowing type aliases to be extended as though they were their
own struct/class.

Unfortunately, I’m not sure how feasible this kind of functionality
would actually be, but if it’s possible then I’m in favor of implementing
it.

On Jun 8, 2017, at 10:14 PM, Yvo van Beek via swift-evolution < >>> swift-evolution@swift.org> wrote:

Typealiases can greatly reduce the complexity of code. But I think one
change in how the compiler handles them could make them even more powerful.

Let's say I'm creating a web server framework and I've created a simple
dictionary to store HTTP headers (I know that headers are more complex than
that, but as an example). I could write something like this:

    typealias HeaderKey = String

  var headers = [HeaderKey: String]()
  headers["Host"] = "domain.com"

Now I can define a couple of default headers like this:

  extension HeaderKey {
    static var lastModified: String { return "Last-Modified" }
    static var host: String { return "Host" }
  }

After that I can do this:

  var headers = [HeaderKey: String]()
  headers[.host] = "domain.com"
  headers[.lastModified] = "some date"
  headers["X-MyHeader"] = "This still works too"

But unfortunately the extension is also applied to normal strings:

    var normalString: String = .host

Perhaps it would be better if the extension would only apply to the
parts of my code where I use the HeaderKey typealias and not to all
Strings. This could be a great tool to specialize classes by creating a
typealias and adding functionality to it. Another example I can think of is
typealiases for dictionaries or arrays with added business logic through
extensions (especially since you can't inherit from structs).

If you want to create an extension that adds functionality to all
Strings you could have created an extension for String instead of HeaderKey.

Please let me know what you think. I'm not sure how complex this change
would be.
I could write a proposal if you're interested.

Kind regards,
Yvo
_______________________________________________
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

_______________________________________________
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

_______________________________________________
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


(Xiaodi Wu) #18

Interesting. So you’d want `newtype Foo = String` to start off with no
members on Foo?

Yeah. Previous discussions of newtype have usually led to discussion of
ways to forward using a protocol-oriented approach. Nothing has gotten too
far, but it usually comes up that suppressing undesired members is
important.

That’s right. These conversations have slipped my mind but that does ring a
bell now.

I think the ask in this thread is for some facility to add members only (or
even, perhaps, to add nothing at all), which would be well served by value
subtyping.

In addition, you’re absolutely right that there’s been discussion of better
ways of forwarding where the starting point is a wrapped type with no
members. Interesting.

It is also important to have some way to distinguish between members with a

···

On Fri, Jun 9, 2017 at 15:47 Matthew Johnson <matthew@anandabits.com> wrote:

On Jun 9, 2017, at 2:39 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
parameter of the underlying type from members that should be treated by
newtype as Self parameters. The mechanism we have for doing that in Swift
happens to be a protocol.

On Fri, Jun 9, 2017 at 15:18 Matthew Johnson <matthew@anandabits.com> > wrote:

On Jun 9, 2017, at 12:09 PM, Xiaodi Wu via swift-evolution < >> swift-evolution@swift.org> wrote:

On Fri, Jun 9, 2017 at 12:44 Robert Bennett via swift-evolution < >> swift-evolution@swift.org> wrote:

Somewhat related to this, shouldn’t it be possible to sub-struct a
struct as long as you only add functions and computed properties (i.e., no
stored properties)? Traditionally structs cannot be subtyped because their
size must be known at compile time. I don’t know the implementation details
of where functions and computed properties live, but something tells me
they belong to the type and not the object (although I’ve never really made
the effort to sit down and fully understand Swift’s type model), in which
case adding them to a struct’s definition would not change the size of the
object on the stack. Thus it should be possible to make custom substructs
of String that add additional functionality but no new stored properties.
Thoughts?

Value subtyping is a large subject and, IIUC, newtype would be a subset
of that topic. Unlikely to be in scope for Swift 5, though, but that’s up
to the core team.

I see newtype as being more related to forwarding than subtyping.
Usually you want to hide significant parts of the interface to the wrapped
type.

On Jun 9, 2017, at 12:12 PM, Jacob Williams via swift-evolution < >>> swift-evolution@swift.org> wrote:

On Jun 9, 2017, at 9:53 AM, Charlie Monroe <charlie@charliemonroe.net> >>> wrote:

-1 - this would disallow e.g. to share UI code between iOS and macOS:

#if os(iOS)
typealias XUView = UIView
#else
typealias XUView = NSView
#endif

extension XUView {
...
}

I really don’t see how this disallows code sharing between the two
systems? Could you explain further? Based on my understanding of the pitch,
this is valid code still. (Although I do like the suggestion of a new
keyword rather than just limiting type alias).

Even if your example was invalid, you could also just do something like
this:

#if os(iOS)
typealias XUView = UIView
extension XUView {
//extension code here
}
#if os(macOS)
typealias XUView = UIView
extension XUView {
// extension code here
}
#endif

While not as pretty, still just as effective if you have to deal with
different types based on the system being compiled for and you could easily
still make the type alias extensions for each type work the same.

On Jun 9, 2017, at 9:53 AM, Charlie Monroe <charlie@charliemonroe.net> >>> wrote:

-1 - this would disallow e.g. to share UI code between iOS and macOS:

#if os(iOS)
typealias XUView = UIView
#else
typealias XUView = NSView
#endif

extension XUView {
...
}

or with any similar compatibility typealiases.

On Jun 9, 2017, at 5:38 PM, Jacob Williams via swift-evolution < >>> swift-evolution@swift.org> wrote:

+1 from me.

There have been times I’ve wanted to subclass an object (such as String)
but since it is a non-class, non-protocol type you can only extend Strings
existing functionality which adds that same functionality to Strings
everywhere. It would be nice if we could either extend type aliases (and
only the type alias), or if it were possible to inherit from structs so
that we could create a custom string type like so:

struct HeaderKey: String {
static var lastModified: String { return “Last-Modified” }
static var host: String { return “Host” }
}

I realize that struct inheritance is far less likely, since that defeats
one of the main pieces of what makes a struct a struct. So I’m all for this
proposal of allowing type aliases to be extended as though they were their
own struct/class.

Unfortunately, I’m not sure how feasible this kind of functionality
would actually be, but if it’s possible then I’m in favor of implementing
it.

On Jun 8, 2017, at 10:14 PM, Yvo van Beek via swift-evolution < >>> swift-evolution@swift.org> wrote:

Typealiases can greatly reduce the complexity of code. But I think one
change in how the compiler handles them could make them even more powerful.

Let's say I'm creating a web server framework and I've created a simple
dictionary to store HTTP headers (I know that headers are more complex than
that, but as an example). I could write something like this:

    typealias HeaderKey = String

  var headers = [HeaderKey: String]()
  headers["Host"] = "domain.com"

Now I can define a couple of default headers like this:

  extension HeaderKey {
    static var lastModified: String { return "Last-Modified" }
    static var host: String { return "Host" }
  }

After that I can do this:

  var headers = [HeaderKey: String]()
  headers[.host] = "domain.com"
  headers[.lastModified] = "some date"
  headers["X-MyHeader"] = "This still works too"

But unfortunately the extension is also applied to normal strings:

    var normalString: String = .host

Perhaps it would be better if the extension would only apply to the
parts of my code where I use the HeaderKey typealias and not to all
Strings. This could be a great tool to specialize classes by creating a
typealias and adding functionality to it. Another example I can think of is
typealiases for dictionaries or arrays with added business logic through
extensions (especially since you can't inherit from structs).

If you want to create an extension that adds functionality to all
Strings you could have created an extension for String instead of HeaderKey.

Please let me know what you think. I'm not sure how complex this change
would be.
I could write a proposal if you're interested.

Kind regards,
Yvo
_______________________________________________
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

_______________________________________________
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

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


(Karl) #19

It’s important to note that you can create powerful quasi-subtype relationships using composition. This is going to be a little divergent, but this is WWDC week and everybody’s here, and its related to the topic of architecture with value-types.

The only practical difference between composition and subclassing is that you can’t add storage and have it carried along with the original “instance”. I’m not even sure that really makes sense for value types, which don’t have identity — the alternative, that a value is be composed of sub-values, makes more sense to me.

I’ve recently converted an entire project from using class hierarchies and stacks of protocols down to a handful of protocols and value-types. I cut the number of files in half, reduced duplicated logic, increased testability, maintainability, all kinds of good stuff. I’m pretty happy with how it turned out, so let me very briefly outline how I did it, to give a concrete example of the kinds of things you can do with composition.

The new data subsystem of this App is now entirely stateless - ultimately, if you look through all of the wrappers, we’re literally just passing around a handful of “let” variables (api endpoint, api key, login key, and a cache identifier), and the entire framework boils down to isolated islands of business logic written in extensions on those structs.

There are only two really important protocols: an ObjectStore (i.e. a cache) and an ObjectSource (i.e. the API). They have very generic methods, like “fetch”, “put” and “delete”. All of the business logic about which queries to run on the cache, and where to put the data, or how to query the correct data out of the API, is written in a collection of wrapper structs which you access to via a computed property (a kind of namespaced protocol-extension, e.g. myObjectStore.userInformation). Above the source and store (i.e. wrapping them) sits a Repository struct, which coordinates getting data from the ObjectStore, querying for that data from the ObjectSource if it doesn’t have anything (or if its expired), and returns a future (actually it’s a Reactive Observable, but any kind of future-like-object will do) encapsulating the operation.

There’s lots you can do with value-types. For example, I created a wrapper for the top-level “Session” type which dynamically checks if the session belongs to a logged-in user. There is a separate repository for public data (e.g. store locations) and private data (e.g. purchase history), and we can model all of this separation really easily in the type-system with no cognitive or computational overhead.

/// An API session, which may or may not belong to a logged-in user.
///
struct Session {
    typealias Identity = (loginKey: String, cacheIdentifier: String)

    let configuration: SessionConfiguration // creates repository on behalf of the session, for unit-testing.
    let identity: Identity?
    let publicData: PublicDataRepository

    init(configuration: SessionConfiguration, identity: Identity) {
        self.configuration = configuration
        self.identity = identity
        self.publicData = configuration.makePublicRepository()
    }
}

/// A session which is dynamically known to belong to a logged-in user.
///
struct AuthenticatedSession {

    let base: Session // you can think of this like ‘super’
    let privateData: PrivateDataRepository

    init?(base: Session) {
        guard let identity = base.identity else { return nil }
        self.base = base
        privateData = base.configuration.makePrivateRepository(for: identity)
    }
}

/* methods which do not require authentication */

extension Session {
    func getStoreLocations() -> Future<[StoreLocation]> { … }
}

/* methods which require a logged-in user */

extension AuthenticatedSession {
    func buySomething(_: ThingToBuy) -> Future<PurchaseReceipt> { … }
}

When it comes to storage, AuthenticatedSession not having the same memory layout as Session means you can’t store one variable that could be either — unless you box it. You can use a protocol to create a semantically-meaningful box (e.g. PublicDataProvider, with one computed property which returns the public-only “Session”), or if you can’t be bothered, you could store it as Any and dynamic-cast to handle all the kinds of values you know how to work with.

It’s a simple model, but it works, its fast, and you can do lots with it if you know how to use it. I feel that sub-typing is really something that requires identity if you want any meaningful benefits over composition.

- Karl

···

On 9. Jun 2017, at 21:47, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

On Jun 9, 2017, at 2:39 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

Interesting. So you’d want `newtype Foo = String` to start off with no members on Foo?

Yeah. Previous discussions of newtype have usually led to discussion of ways to forward using a protocol-oriented approach. Nothing has gotten too far, but it usually comes up that suppressing undesired members is important.

It is also important to have some way to distinguish between members with a parameter of the underlying type from members that should be treated by newtype as Self parameters. The mechanism we have for doing that in Swift happens to be a protocol.


(Matthew Johnson) #20

+1 to this. My ideal imagining of this behavior is to:

1) Define a protocol containing the precise operations you want to support via forwarding.
2) Internally/fileprivately extend the original type to conform to this protocol.
3) In your new type, tell it to forward protocol members for a particular property (the underlying String, or whatever) and the compiler will synthesize the protocol member stubs on the new type.

There are definitely finer details to be worked out but that's a rough sketch of how I've been imagining it could work. Nice advantages include (1) if there already exists a protocol that defines the exact operations you want to forward, you can skip step 2, and (2) it's explicit/opt-in synthesis where you have to provide the members to forward (this is expected, at a minimum), but you don't have to write all the boilerplate implementations.

I have a mostly finished second draft of a protocol-oriented forwarding proposal which is pretty similar to what you describe here. The most important difference is that while protocols are used to perform forwarding neither type is required to actually conform to the protocol.

That turns out to be pretty important when you think about edge cases. Sometimes you don’t actually want the forwardee to conform (that’s probably why you said internal / fileprivate). Sometimes you also don’t want the forwarder to conform. IIRC, there are also cases where it cannot conform (I would have to dig up the proposal to recall the details).

···

On Jun 9, 2017, at 2:53 PM, Tony Allevato <tony.allevato@gmail.com> wrote:

On Fri, Jun 9, 2017 at 12:47 PM Matthew Johnson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jun 9, 2017, at 2:39 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

Interesting. So you’d want `newtype Foo = String` to start off with no members on Foo?

Yeah. Previous discussions of newtype have usually led to discussion of ways to forward using a protocol-oriented approach. Nothing has gotten too far, but it usually comes up that suppressing undesired members is important.

It is also important to have some way to distinguish between members with a parameter of the underlying type from members that should be treated by newtype as Self parameters. The mechanism we have for doing that in Swift happens to be a protocol.

On Fri, Jun 9, 2017 at 15:18 Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> wrote:

On Jun 9, 2017, at 12:09 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Fri, Jun 9, 2017 at 12:44 Robert Bennett via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Somewhat related to this, shouldn’t it be possible to sub-struct a struct as long as you only add functions and computed properties (i.e., no stored properties)? Traditionally structs cannot be subtyped because their size must be known at compile time. I don’t know the implementation details of where functions and computed properties live, but something tells me they belong to the type and not the object (although I’ve never really made the effort to sit down and fully understand Swift’s type model), in which case adding them to a struct’s definition would not change the size of the object on the stack. Thus it should be possible to make custom substructs of String that add additional functionality but no new stored properties. Thoughts?

Value subtyping is a large subject and, IIUC, newtype would be a subset of that topic. Unlikely to be in scope for Swift 5, though, but that’s up to the core team.

I see newtype as being more related to forwarding than subtyping. Usually you want to hide significant parts of the interface to the wrapped type.

On Jun 9, 2017, at 12:12 PM, Jacob Williams via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jun 9, 2017, at 9:53 AM, Charlie Monroe <charlie@charliemonroe.net <mailto:charlie@charliemonroe.net>> wrote:

-1 - this would disallow e.g. to share UI code between iOS and macOS:

#if os(iOS)
  typealias XUView = UIView
#else
  typealias XUView = NSView
#endif

extension XUView {
  ...
}

I really don’t see how this disallows code sharing between the two systems? Could you explain further? Based on my understanding of the pitch, this is valid code still. (Although I do like the suggestion of a new keyword rather than just limiting type alias).

Even if your example was invalid, you could also just do something like this:

#if os(iOS)
  typealias XUView = UIView
  extension XUView {
    //extension code here
  }
#if os(macOS)
  typealias XUView = UIView
  extension XUView {
    // extension code here
  }
#endif

While not as pretty, still just as effective if you have to deal with different types based on the system being compiled for and you could easily still make the type alias extensions for each type work the same.

On Jun 9, 2017, at 9:53 AM, Charlie Monroe <charlie@charliemonroe.net <mailto:charlie@charliemonroe.net>> wrote:

-1 - this would disallow e.g. to share UI code between iOS and macOS:

#if os(iOS)
  typealias XUView = UIView
#else
  typealias XUView = NSView
#endif

extension XUView {
  ...
}

or with any similar compatibility typealiases.

On Jun 9, 2017, at 5:38 PM, Jacob Williams via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

+1 from me.

There have been times I’ve wanted to subclass an object (such as String) but since it is a non-class, non-protocol type you can only extend Strings existing functionality which adds that same functionality to Strings everywhere. It would be nice if we could either extend type aliases (and only the type alias), or if it were possible to inherit from structs so that we could create a custom string type like so:

struct HeaderKey: String {
  static var lastModified: String { return “Last-Modified” }
  static var host: String { return “Host” }
}

I realize that struct inheritance is far less likely, since that defeats one of the main pieces of what makes a struct a struct. So I’m all for this proposal of allowing type aliases to be extended as though they were their own struct/class.

Unfortunately, I’m not sure how feasible this kind of functionality would actually be, but if it’s possible then I’m in favor of implementing it.

On Jun 8, 2017, at 10:14 PM, Yvo van Beek via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Typealiases can greatly reduce the complexity of code. But I think one change in how the compiler handles them could make them even more powerful.

Let's say I'm creating a web server framework and I've created a simple dictionary to store HTTP headers (I know that headers are more complex than that, but as an example). I could write something like this:

    typealias HeaderKey = String

  var headers = [HeaderKey: String]()
  headers["Host"] = "domain.com <http://domain.com/>"

Now I can define a couple of default headers like this:

  extension HeaderKey {
    static var lastModified: String { return "Last-Modified" }
    static var host: String { return "Host" }
  }

After that I can do this:

  var headers = [HeaderKey: String]()
  headers[.host] = "domain.com <http://domain.com/>"
  headers[.lastModified] = "some date"
  headers["X-MyHeader"] = "This still works too"

But unfortunately the extension is also applied to normal strings:

    var normalString: String = .host

Perhaps it would be better if the extension would only apply to the parts of my code where I use the HeaderKey typealias and not to all Strings. This could be a great tool to specialize classes by creating a typealias and adding functionality to it. Another example I can think of is typealiases for dictionaries or arrays with added business logic through extensions (especially since you can't inherit from structs).

If you want to create an extension that adds functionality to all Strings you could have created an extension for String instead of HeaderKey.

Please let me know what you think. I'm not sure how complex this change would be.
I could write a proposal if you're interested.

Kind regards,
Yvo
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

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

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

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

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