3

I have the following (I've simplified it to the most basic case):

class Something<H: Hashable> {

}

func function<H: NSDate>() -> Something<H> {
    let s: Something<NSDate> = Something()
    return s
}

The error is on the return s line:

NSDate is not identical to 'H'

But this doesn't make an sense as any subclass of NSDate or NSDate itself should be allowed by this.

For example the following works:

let dateSomething: Something<NSDate> = Something()
functionWorks(dateSomething)

func functionWorks<H: NSDate>(parameter: Something<H>) {

}

Does anyone have any ideas why the first example isn't working? I'm currently thinking it might be an issue with Swift itself...

Rich
  • 7,816
  • 5
  • 42
  • 56

3 Answers3

5

Generic type parameters don't unify. Type constraints are checked only when specializing a generic, and otherwise two thing<T> with different things inside the angle brackets are completely unrelated types, even if the things inside the angle brackets are themselves related types.

To put it another way: Swift generics are type preserving. If someone calls function<SubclassOfNSDate>(), it needs to return Something<SubclassOfNSDate>, not Something<NSDate>. To return the latter would erase the type the caller provided. (Some other languages do generics with type erasure, so you could do this there. But type erasure systems have other drawbacks, so Swift doesn't go that way.)

It's hard to see what you're really trying to accomplish here from what you've posted. Changing this line should at least make it compile, though:

let s: Something<H> = Something()

This way you're using the H (which happens to be NSDate) from the function's generic parameter (which is what you're expected to return).

rickster
  • 118,448
  • 25
  • 255
  • 308
  • "Some other languages do generics with type erasure, so you could do this there": Would Java fall under this category? – Rich Feb 20 '15 at 15:55
  • Yes. You can read more about that in this question: http://stackoverflow.com/questions/339699/java-generics-type-erasure-when-and-what-happens. You may also find this background useful: http://en.wikipedia.org/wiki/Generics_in_Java#Problems_with_type_erasure – Rob Napier Feb 20 '15 at 16:06
  • Ah thats probably why I thought you could do it (I've got a Java background)! Thanks! :) – Rich Feb 20 '15 at 16:18
5

To get back to what you're asking for:

func function<H: NSDate>() -> Something<H> {

As Nate and rickster note, this won't work because of Swift's type rules. But it's also not really what you mean. There is no reason to use type parameterization to indicate one specific type. You don't need to say "H, where H is NSDate." You should just say "NSDate." What you really meant was:

func function() -> Something<NSDate> {

And that will work fine. In fact, because of type inference, it's even simpler:

func function() -> Something<NSDate> {
    return Something()
}
Rob Napier
  • 250,948
  • 34
  • 393
  • 528
  • 1
    What about my case, where you want to subtype `Something` with a subclass of `NSDate`? When I try my code I get a "`Something` is not convertible to `Something`" error, since the type system doesn't recognize class inheritance inside a generic subtype. (Excepting `Array`, which is special.) – Nate Cook Feb 20 '15 at 16:59
  • Swift generics are not covariant (which is your point). If you do it my way, then a `Something` is free to hold a `SpecialDate` (but you can't rely on its specialness). Doing it your way can create a `Something` which is not covariant with `Something`. That could be useful in some cases, but I expect in this case, the former is what was really desired. https://gist.github.com/rnapier/1653e4272d9d303fa1eb – Rob Napier Feb 20 '15 at 17:41
  • I let @Rich's function signatures stand in my answer because I'm not sure if they're really what he meant (and there's some part of his code he's not showing that requires them), or something declared elsewhere that he has to be constrained by. But you're right that if all you need is a function that generates a `Something`, the simpler signatures in this answer will suffice. – rickster Feb 20 '15 at 19:54
  • 1
    It's totally a gut feeling; my experience with other Swift devs (and Java devs) is that "" is usually a mistake rather than the intent (vs. ""). But your answer and Nate's answer certain address the case were it isn't a mistake. – Rob Napier Feb 20 '15 at 20:12
  • I think I might have over simplified my use case too much! The original (much longer) used function (hence the H!) and no I didn't want to just return Something, in fact I don't care about the generic type, I just want to call methods on the Something container class. I also think I ran into the problem @NateCook described in the first comment. I've since refactored anyway with a better solution that doesn't apply to this question anymore! If I get some time, I'll try to either come up with a better example of what I wanted to do or just post everything... – Rich Feb 21 '15 at 00:55
4

It doesn't work because the particular subtype H for a call to your function could be an NSDate subclass:

// subclass NSDate
class SpecialDate : NSDate { }

// this needs a Something<SpecialDate> returned, not a Something<NSDate>
let specialSomething: Something<SpecialDate> = function()

The solution is to use H to subtype Something in your function:

func function<H: NSDate>() -> Something<H> {
    let s: Something<H> = Something()
    return s
}
Nate Cook
  • 87,949
  • 32
  • 210
  • 173