[swift-evolution] Overloading Generic Types

Slava Pestov spestov at apple.com
Wed Feb 22 17:42:19 CST 2017


> On Feb 22, 2017, at 3:39 PM, David Sweeris <davesweeris at mac.com> wrote:
> 
>> 
>> On Feb 22, 2017, at 3:00 PM, Slava Pestov <spestov at apple.com <mailto:spestov at apple.com>> wrote:
>> 
>> 
>>> On Dec 23, 2016, at 12:32 PM, David Sweeris via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>> 
>>> (I feel like I’ve already written this... I looked through my sent mail and didn’t see anything, but my sincerest apologies if I started this thread a month ago and forgot about it or something.)
>>> 
>>> I no longer recall exactly what first made me want to do this (probably something in my on-going “teach the compiler calculus” project), but I’ve been thinking lately that it could be quite handy to overload types themselves based on the value of generic parameters. As a somewhat contrived example:
>>> struct Array <T> { /*everything exactly as it is now*/ }
>>> struct Array <T> where T == Bool { /* packs every 8 bools into a UInt8 for more efficient storage */ }
>>> 
>>> We can already do this with functions… Conceptually this isn’t any different.
>> 
>> Actually this is a major complication because of the runtime generics model. Imagine you have a function that takes a T and constructs an Array<T>. Now it has to do dynamic dispatch to find the right type metadata (is it the “generic” Array<T>, or the specialized Array<Bool>?)
> 
> Wouldn’t that get resolved at compile time? Oh! Wait, are you talking about this?
> func bar <T> (_ x: T) -> String { return "any T" }
> func bar <T> (_ x: T) -> String where T: CustomStringConvertible { return "\(x)" }
> func foo <T> (_ x: T) -> String { return bar(x) }
> and “foo()" always returns “any T” because by the time we get to `bar`, T isn’t known to be constrained to anything at compile time?

I mean this:

func foo<T>(t: T) -> [T] {
  return [t]
}

foo(t: false) // is this the ‘optimized’ or ‘unoptimized’ version?
foo(t: 123)

> 
> In any case, since the point is to optimize the type's implementation rather than change its behavior, I think the only result is that your array maker function would return an “unoptimized” array.

Ok then, what if I have:

func foo<T>(x: T, y: T) {

}

And I call foo() with an ‘optimized’ Array<Bool> and ‘unoptimized’ Array<Bool>. That won’t work at all, since it’s only passing one type metadata parameter for T, and not one for each value. So which one would it pass?

> I’m not sure… I’d have to think about it some more. And come up with a better illustration, since it’s been pointed out that my original example isn’t a particularly good idea.
> 
>>> As long as the specific version exposes everything the generic version does (easy for the compiler to enforce), I think everything would just work (famous last words).
>> 
>> What if another module defines an extension of Array<T>, but not ‘Array<T> where T == Bool’?
> 
> IIUC, that shouldn’t matter because the specific version would have to have the same public interface as the generic version.

But with the extension in place, now they have different interfaces, because the optimized version won’t have the extension’s methods in it.

> 
>>> In this example, the subscript function would need to extract the specified bit and return it as a Bool instead of simply returning the specified element. The `Element` typealias would be `Bool` instead of `UInt8`, which would mean the size/stride might be different than expected (but that’s why we have `MemoryLayout<>`).
>>> 
>>> Anyway, because generic structs & functions already can’t make assumptions about a generic argument (beyond any constraints, of course), I think this should be in phase 2… but I’m quite hazy on how generics work once the code’s been compiled, so maybe not.
>> 
>> Another way of modeling this might be to define a protocol, say “RepresentableInArray”, which implements get and set methods that take a pointer to a buffer, and an index. A default implementation in a protocol extension would just load and store the value. ‘Bool’ would define its own conformance which performs bitwise operations.
>> 
>> However I think this is out of scope for stage 2 — it doesn’t fix any obvious major shortcoming in the language, it vastly complicates the implementation and it’s not required to achieve our ABI stability goals.
> 
> Fair enough. Is there a stage 3, or does that mean “out of scope until Swift 4.1+”?

The latter.

Slava

> 
> - Dave Sweeris

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170222/a67db3e0/attachment.html>


More information about the swift-evolution mailing list