How do generics interact with overloaded methods?


(Ryan Conway) #1

Hey swift-users,

I'm teaching myself Swift, coming from a mostly C and Python background,
and would like to understand generics more deeply. Right now, I'm seeing
generic data types invoke overloaded methods in ways I do not understand,
and am seeking clarification why.

In an effort to model a data structure whose data can be represented as
multiple data types simultaneously, I've made this class. Here its
implementation is mocked using constants.

class Bar {
    func read() -> Int {
        return -1
    }
    func read() -> UInt {
        return 1
    }
    func read<T>() -> T {
        print("Unsupported data type requested")
        exit(1)
    }
}

Objects of that class return the requested type as expected when used like
so:

let thisWorks: Int = Bar().read() // returns -1
let thisAlsoWorks: UInt = Bar().read() // returns 1

However, when I introduce generics on top of that class, the expected
method (the "most precise" method) is not called. For example, given this
other class:

class Baz<T> {
    let myBar = Bar()

    func read() -> T {
        return self.myBar.read()
    }
}

Both of these invocations call the generic read<T>() -> T method rather
than the read() -> UInt method:

let thisDoesntWork = Baz<UInt>().read()
let thisDoesntWorkEither: UInt = Baz<UInt>().read()

Am I using generics wrong here? Is there some other language feature I
should be using to capture this data? Any pointers would be greatly
appreciated.

Thank you,
Ryan


(David Turnbull) #2

You're using generics like they were templates. A lot of people coming from
C++ will make this mistake at first. Here's the secret: C++ templates are a
substitution at compile time. Swift generics have to satisfy the protocols.

Your example is contrived to show exactly how templates work and generics
don't work. It's not fixable. But here's some code to ponder that will lead
you down the right path...

func read<T:UnsignedIntegerType>() -> T { return 1 }

func read<T:SignedIntegerType>() -> T { return -1 }

let x:Int64 = read()

let y:UInt8 = read()

Protocols are essential to generics. Figure out how they work together and
you're good to go.

-david

···

On Wed, Jan 13, 2016 at 11:04 PM, Ryan Conway via swift-users < swift-users@swift.org> wrote:

Hey swift-users,

I'm teaching myself Swift, coming from a mostly C and Python background,
and would like to understand generics more deeply. Right now, I'm seeing
generic data types invoke overloaded methods in ways I do not understand,
and am seeking clarification why.

In an effort to model a data structure whose data can be represented as
multiple data types simultaneously, I've made this class. Here its
implementation is mocked using constants.

class Bar {
    func read() -> Int {
        return -1
    }
    func read() -> UInt {
        return 1
    }
    func read<T>() -> T {
        print("Unsupported data type requested")
        exit(1)
    }
}

Objects of that class return the requested type as expected when used like
so:

let thisWorks: Int = Bar().read() // returns -1
let thisAlsoWorks: UInt = Bar().read() // returns 1

However, when I introduce generics on top of that class, the expected
method (the "most precise" method) is not called. For example, given this
other class:

class Baz<T> {
    let myBar = Bar()

    func read() -> T {
        return self.myBar.read()
    }
}

Both of these invocations call the generic read<T>() -> T method rather
than the read() -> UInt method:

let thisDoesntWork = Baz<UInt>().read()
let thisDoesntWorkEither: UInt = Baz<UInt>().read()

Am I using generics wrong here? Is there some other language feature I
should be using to capture this data? Any pointers would be greatly
appreciated.

Thank you,
Ryan

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


(Wallacy) #3

Generics are compile time feature, but like said, not like c++ templates.
Unconstrained generic T will be like Any. " return self.myBar.read()" can
only be translated to a "function pointer" to the generic version, because
is the only information available at compile time.
Has a talk on wwdc explaining this.
Swift can do generic specialization, but only when information enough to do
this. Usually constrained generic is the way to go.

···

Em qui, 14 de jan de 2016 às 05:05, Ryan Conway via swift-users < swift-users@swift.org> escreveu:

Hey swift-users,

I'm teaching myself Swift, coming from a mostly C and Python background,
and would like to understand generics more deeply. Right now, I'm seeing
generic data types invoke overloaded methods in ways I do not understand,
and am seeking clarification why.

In an effort to model a data structure whose data can be represented as
multiple data types simultaneously, I've made this class. Here its
implementation is mocked using constants.

class Bar {
    func read() -> Int {
        return -1
    }
    func read() -> UInt {
        return 1
    }
    func read<T>() -> T {
        print("Unsupported data type requested")
        exit(1)
    }
}

Objects of that class return the requested type as expected when used like
so:

let thisWorks: Int = Bar().read() // returns -1
let thisAlsoWorks: UInt = Bar().read() // returns 1

However, when I introduce generics on top of that class, the expected
method (the "most precise" method) is not called. For example, given this
other class:

class Baz<T> {
    let myBar = Bar()

    func read() -> T {
        return self.myBar.read()
    }
}

Both of these invocations call the generic read<T>() -> T method rather
than the read() -> UInt method:

let thisDoesntWork = Baz<UInt>().read()
let thisDoesntWorkEither: UInt = Baz<UInt>().read()

Am I using generics wrong here? Is there some other language feature I
should be using to capture this data? Any pointers would be greatly
appreciated.

Thank you,
Ryan
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Zhao Xin) #4

Hi Ryan,

I just try to explain this to you. I don't know Python. That is my
background.

I think in you code

class Baz<T> {
    let myBar = Bar()

    func read() -> T {
        return self.myBar.read()
    }
}

func read() -> T means you want the return type T method other than the
other two. Swift will not try the return type Int as it choose the function
read() -> T before it replaces the type T to type Int. That is my option.

Further more, if you change code as

class Baz<T> {
    let myBar = Bar()

    func read() -> T {
        return self.myBar.read()
    }

    func read() -> Int {
        return self.myBar.read()
    }
    func read() -> UInt {
        return 1
    }
}

//let thisDoesntWork = Baz<UInt>().read() // ambitious code as swift does
not know which function to choose
let thisWillWork:Int = Baz<UInt>().read() // -1

zhaoxin

···

On Thu, Jan 14, 2016 at 3:04 PM, Ryan Conway via swift-users < swift-users@swift.org> wrote:

Hey swift-users,

I'm teaching myself Swift, coming from a mostly C and Python background,
and would like to understand generics more deeply. Right now, I'm seeing
generic data types invoke overloaded methods in ways I do not understand,
and am seeking clarification why.

In an effort to model a data structure whose data can be represented as
multiple data types simultaneously, I've made this class. Here its
implementation is mocked using constants.

class Bar {
    func read() -> Int {
        return -1
    }
    func read() -> UInt {
        return 1
    }
    func read<T>() -> T {
        print("Unsupported data type requested")
        exit(1)
    }
}

Objects of that class return the requested type as expected when used like
so:

let thisWorks: Int = Bar().read() // returns -1
let thisAlsoWorks: UInt = Bar().read() // returns 1

However, when I introduce generics on top of that class, the expected
method (the "most precise" method) is not called. For example, given this
other class:

class Baz<T> {
    let myBar = Bar()

    func read() -> T {
        return self.myBar.read()
    }
}

Both of these invocations call the generic read<T>() -> T method rather
than the read() -> UInt method:

let thisDoesntWork = Baz<UInt>().read()
let thisDoesntWorkEither: UInt = Baz<UInt>().read()

Am I using generics wrong here? Is there some other language feature I
should be using to capture this data? Any pointers would be greatly
appreciated.

Thank you,
Ryan

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

--

Owen Zhao


(Ryan Conway) #5

Thank you for your responses. I'm grateful for having been given several
different perspectives, especially in such a short time, and have some
ideas for things to try this weekend.

If I'm understanding correctly, the salient point is that Swift will
resolve a call to an overloaded function to exactly one implementation of
that overloaded function, even if for all types of a generic class' generic
function.

I'd like to understand Swift's design philosophy better. Why were things
implemented this way? For example, in the following line of code:

let thisDoesntWork = Baz<UInt>().read()

The compiler knows that read() is being called on Baz<UInt>, so it could
know that read() must return a UInt, so it could know that it can safely
use the Bar.read() that returns a UInt. From what I'm hearing, this is what
C++ would use, but not Swift, and I imagine there is a good reason for it.

Wallacy, if you're able to provide a link to this WWDC talk I would greatly
appreciate it.

Ryan

···

On Thu, Jan 14, 2016 at 6:07 AM, Wallacy <wallacyf@gmail.com> wrote:

Generics are compile time feature, but like said, not like c++ templates.
Unconstrained generic T will be like Any. " return self.myBar.read()" can
only be translated to a "function pointer" to the generic version, because
is the only information available at compile time.
Has a talk on wwdc explaining this.
Swift can do generic specialization, but only when information enough to
do this. Usually constrained generic is the way to go.

Em qui, 14 de jan de 2016 às 05:05, Ryan Conway via swift-users < > swift-users@swift.org> escreveu:

Hey swift-users,

I'm teaching myself Swift, coming from a mostly C and Python background,
and would like to understand generics more deeply. Right now, I'm seeing
generic data types invoke overloaded methods in ways I do not understand,
and am seeking clarification why.

In an effort to model a data structure whose data can be represented as
multiple data types simultaneously, I've made this class. Here its
implementation is mocked using constants.

class Bar {
    func read() -> Int {
        return -1
    }
    func read() -> UInt {
        return 1
    }
    func read<T>() -> T {
        print("Unsupported data type requested")
        exit(1)
    }
}

Objects of that class return the requested type as expected when used
like so:

let thisWorks: Int = Bar().read() // returns -1
let thisAlsoWorks: UInt = Bar().read() // returns 1

However, when I introduce generics on top of that class, the expected
method (the "most precise" method) is not called. For example, given this
other class:

class Baz<T> {
    let myBar = Bar()

    func read() -> T {
        return self.myBar.read()
    }
}

Both of these invocations call the generic read<T>() -> T method rather
than the read() -> UInt method:

let thisDoesntWork = Baz<UInt>().read()
let thisDoesntWorkEither: UInt = Baz<UInt>().read()

Am I using generics wrong here? Is there some other language feature I
should be using to capture this data? Any pointers would be greatly
appreciated.

Thank you,
Ryan
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Zhao Xin) #6

The compiler knows that read() is being called on Baz<UInt>, so it could
know that read() must return a UInt,

I think there are two steps here.

   1. compiler choose func read<T>() -> T base on the return type of func
   read() -> T
   2. compiler replace T with UInt

In your mind, the steps are reversed.

In fact, there is no overload here.

Code:

let thisDoesntWork = Baz<UInt>().myBar.read() // error: ambiguous use of
'read()'

Compiler doesn't know which function to choose as there is no return type
to derive from。

z
​haoxin​

···

On Fri, Jan 15, 2016 at 2:13 AM, Ryan Conway via swift-users < swift-users@swift.org> wrote:

Thank you for your responses. I'm grateful for having been given several
different perspectives, especially in such a short time, and have some
ideas for things to try this weekend.

If I'm understanding correctly, the salient point is that Swift will
resolve a call to an overloaded function to exactly one implementation of
that overloaded function, even if for all types of a generic class' generic
function.

I'd like to understand Swift's design philosophy better. Why were things
implemented this way? For example, in the following line of code:

let thisDoesntWork = Baz<UInt>().read()

The compiler knows that read() is being called on Baz<UInt>, so it could
know that read() must return a UInt, so it could know that it can safely
use the Bar.read() that returns a UInt. From what I'm hearing, this is what
C++ would use, but not Swift, and I imagine there is a good reason for it.

Wallacy, if you're able to provide a link to this WWDC talk I would
greatly appreciate it.

Ryan

On Thu, Jan 14, 2016 at 6:07 AM, Wallacy <wallacyf@gmail.com> wrote:

Generics are compile time feature, but like said, not like c++ templates.
Unconstrained generic T will be like Any. " return self.myBar.read()" can
only be translated to a "function pointer" to the generic version, because
is the only information available at compile time.
Has a talk on wwdc explaining this.
Swift can do generic specialization, but only when information enough to
do this. Usually constrained generic is the way to go.

Em qui, 14 de jan de 2016 às 05:05, Ryan Conway via swift-users < >> swift-users@swift.org> escreveu:

Hey swift-users,

I'm teaching myself Swift, coming from a mostly C and Python background,
and would like to understand generics more deeply. Right now, I'm seeing
generic data types invoke overloaded methods in ways I do not understand,
and am seeking clarification why.

In an effort to model a data structure whose data can be represented as
multiple data types simultaneously, I've made this class. Here its
implementation is mocked using constants.

class Bar {
    func read() -> Int {
        return -1
    }
    func read() -> UInt {
        return 1
    }
    func read<T>() -> T {
        print("Unsupported data type requested")
        exit(1)
    }
}

Objects of that class return the requested type as expected when used
like so:

let thisWorks: Int = Bar().read() // returns -1
let thisAlsoWorks: UInt = Bar().read() // returns 1

However, when I introduce generics on top of that class, the expected
method (the "most precise" method) is not called. For example, given this
other class:

class Baz<T> {
    let myBar = Bar()

    func read() -> T {
        return self.myBar.read()
    }
}

Both of these invocations call the generic read<T>() -> T method rather
than the read() -> UInt method:

let thisDoesntWork = Baz<UInt>().read()
let thisDoesntWorkEither: UInt = Baz<UInt>().read()

Am I using generics wrong here? Is there some other language feature I
should be using to capture this data? Any pointers would be greatly
appreciated.

Thank you,
Ryan
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

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

--

Owen Zhao


(Wallacy) #7

Sure,

Talk: https://developer.apple.com/videos/play/wwdc2015-409/

There is a part called: How Generics Work

There is another talk with other details, but cannot remember which.

And also is a doc:
https://github.com/apple/swift/blob/master/docs/Generics.rst

May you can see: Generics is more about protocols than "type substitution".

The point is: Unconstrained T, can be anything, so when compiler try to
compile this:

func read() -> T {
        return self.myBar.read()
}

self.myBar.read() can only point to generic version. The overload will be
resolved once.

The compiler will not generate another version of this read() -> T for
every single call of Baz<T>.

Usually, unconstrained T and "Any" is the same thing. Generics is more
about "which overload Im will call at this point".

Generic specialization is a optimization phase.

···

Em qui, 14 de jan de 2016 às 16:13, Ryan Conway <theconmethod@gmail.com> escreveu:

Thank you for your responses. I'm grateful for having been given several
different perspectives, especially in such a short time, and have some
ideas for things to try this weekend.

If I'm understanding correctly, the salient point is that Swift will
resolve a call to an overloaded function to exactly one implementation of
that overloaded function, even if for all types of a generic class' generic
function.

I'd like to understand Swift's design philosophy better. Why were things
implemented this way? For example, in the following line of code:

let thisDoesntWork = Baz<UInt>().read()

The compiler knows that read() is being called on Baz<UInt>, so it could
know that read() must return a UInt, so it could know that it can safely
use the Bar.read() that returns a UInt. From what I'm hearing, this is what
C++ would use, but not Swift, and I imagine there is a good reason for it.

Wallacy, if you're able to provide a link to this WWDC talk I would
greatly appreciate it.

Ryan

On Thu, Jan 14, 2016 at 6:07 AM, Wallacy <wallacyf@gmail.com> wrote:

Generics are compile time feature, but like said, not like c++ templates.
Unconstrained generic T will be like Any. " return self.myBar.read()" can
only be translated to a "function pointer" to the generic version, because
is the only information available at compile time.
Has a talk on wwdc explaining this.
Swift can do generic specialization, but only when information enough to
do this. Usually constrained generic is the way to go.

Em qui, 14 de jan de 2016 às 05:05, Ryan Conway via swift-users < >> swift-users@swift.org> escreveu:

Hey swift-users,

I'm teaching myself Swift, coming from a mostly C and Python background,
and would like to understand generics more deeply. Right now, I'm seeing
generic data types invoke overloaded methods in ways I do not understand,
and am seeking clarification why.

In an effort to model a data structure whose data can be represented as
multiple data types simultaneously, I've made this class. Here its
implementation is mocked using constants.

class Bar {
    func read() -> Int {
        return -1
    }
    func read() -> UInt {
        return 1
    }
    func read<T>() -> T {
        print("Unsupported data type requested")
        exit(1)
    }
}

Objects of that class return the requested type as expected when used
like so:

let thisWorks: Int = Bar().read() // returns -1
let thisAlsoWorks: UInt = Bar().read() // returns 1

However, when I introduce generics on top of that class, the expected
method (the "most precise" method) is not called. For example, given this
other class:

class Baz<T> {
    let myBar = Bar()

    func read() -> T {
        return self.myBar.read()
    }
}

Both of these invocations call the generic read<T>() -> T method rather
than the read() -> UInt method:

let thisDoesntWork = Baz<UInt>().read()
let thisDoesntWorkEither: UInt = Baz<UInt>().read()

Am I using generics wrong here? Is there some other language feature I
should be using to capture this data? Any pointers would be greatly
appreciated.

Thank you,
Ryan
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users