[Discussion] Additional generics specialization


(Adrian Zubarev) #1

This is something additional, but I’m curios about how the community feels about it.

I recently come across the issue where conforming to Hashable wasn’t enough to thecke if two instances of the same generic type were equal.

I had additionally provide myself the != function.

public func !=<T, U>(lhs: SomeTypeName<T>, rhs: SomeTypeName<U>) -> Bool {
    return lhs.hashValue != rhs.hashValue
}
I wondered if Swift can ever get generic specialization like this:

public func !=<T : Hashable, U, V>(lhs: T<U>, rhs: T<V>) -> Bool {
    return !(lhs.hashValue == rhs.hashValue)
}
This function in stdlib would fill the gap. Or we need an extra protocol GenericHashable which includes !=.

···

--
Adrian Zubarev
Sent with Airmail


(Charlie Monroe) #2

In general, Swift will need a way to point to a generic type without a particular specialization. Currently, you can't do this:

class APICall<T> { }

class CombinedCall: APICall<Bool> {
  var calls: [APICall] // Error - no specialization
}

I've been currently working around this by:

class APICallBase {} // No generics
class APICall<T>: APICallBase {}
class CombinedCall: APICall<Bool> {
  var calls: [APICallBase]
}

But it's definitely a pain to work with.

As asked by the core team several times now, we should defer any discussion on additional features until August.

···

On Jul 18, 2016, at 9:04 AM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

This is something additional, but I’m curios about how the community feels about it.

I recently come across the issue where conforming to Hashable wasn’t enough to thecke if two instances of the same generic type were equal.

I had additionally provide myself the != function.

public func !=<T, U>(lhs: SomeTypeName<T>, rhs: SomeTypeName<U>) -> Bool {
    return lhs.hashValue != rhs.hashValue
}
I wondered if Swift can ever get generic specialization like this:

public func !=<T : Hashable, U, V>(lhs: T<U>, rhs: T<V>) -> Bool {
    return !(lhs.hashValue == rhs.hashValue)
}
This function in stdlib would fill the gap. Or we need an extra protocol GenericHashable which includes !=.

--
Adrian Zubarev
Sent with Airmail

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


(Haravikk) #3

What exactly is the problem you're trying to solve here? Hash values being equal is not a guarantee of equality, so what you're doing with this operator is masking what's going on which I'm not sure is a good idea. If you need to compare hash-values, then compare them, they're already as generic as you can possibly get (since they're always of type Int), otherwise you can't really rely on hash-values from generic types in this way, and shouldn't be hiding them behind the equality operator.

To think of it another way, if two hash-values are equal, then the two values *might* be equal, but you still have to test them further to be sure. You can only rely on the hash-value in this way if you have control of the implementation details, which you can only guarantee when the values you are comparing are the same type, or from a family of types that you control (as you say, a protocol could do this, by requiring that hash-values are unique within some well-defined domain).

I feel like this is seeking a solution that is a workaround to a protocol that has no associatedtype, as the problem you're describing is essentially already solved by generic constraints. For example, if you were working with Iterators you can define things like:

  func someFunc<I1:IteratorProtocol, I2:IteratorProtocol where I1.Element:Hashable, I2.Element:Hashable>(lhs:I1, rhs:I2) -> Bool {
    return lhs.next()?.hashValue == rhs.next()?.hashValue
  }

Probably a useless example, but it shows how generic constraints achieve this already, but they require a protocol that is intended to expose an internal detail (the Element associatedtype), this is not what Hashable is for, Hashable is for reducing any conforming type down to an Int, with no guarantee of that value being unique.

···

On 18 Jul 2016, at 08:04, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

This is something additional, but I’m curios about how the community feels about it.

I recently come across the issue where conforming to Hashable wasn’t enough to thecke if two instances of the same generic type were equal.

I had additionally provide myself the != function.

public func !=<T, U>(lhs: SomeTypeName<T>, rhs: SomeTypeName<U>) -> Bool {
    return lhs.hashValue != rhs.hashValue
}
I wondered if Swift can ever get generic specialization like this:

public func !=<T : Hashable, U, V>(lhs: T<U>, rhs: T<V>) -> Bool {
    return !(lhs.hashValue == rhs.hashValue)
}
This function in stdlib would fill the gap. Or we need an extra protocol GenericHashable which includes !=.


(L Mihalkovic) #4

Regards
(From mobile)

This is something additional, but I’m curios about how the community feels about it.

I recently come across the issue where conforming to Hashable wasn’t enough to thecke if two instances of the same generic type were equal.

I had additionally provide myself the != function.

public func !=<T, U>(lhs: SomeTypeName<T>, rhs: SomeTypeName<U>) -> Bool {
    return lhs.hashValue != rhs.hashValue
}
I wondered if Swift can ever get generic specialization like this:

public func !=<T : Hashable, U, V>(lhs: T<U>, rhs: T<V>) -> Bool {
    return !(lhs.hashValue == rhs.hashValue)
}

I am not sure about the ordering... Interestingly enough, this is real code written last week as part of a framework:

Export function newSataStore<T extends DataStore<U>, U> (ctor: { new (config:DataStoreConfig):T, config:DataStoreConfig):T {
   return new ctor(config)
}

And this is legal code just to try:

interface Hashable {
  getHash(): number
}
interface T extends Hashable { }
class SomeTypeName<Type extends Hashable> {
  public getHash():number {
    return ...;
  }
  public type():this {
    return Type;
  }
}
function compare<T1 extends T, T2 extends T>(lhs:SomeTypeName<T1>, rhs:SomeTypeName<T2>):boolean {
  return lhs.getHash() != rhs.getHash();
}

but this is more interesting:

type List<T> = T & { next: List<T> } // listObj.next.next.next.???

···

On Jul 18, 2016, at 9:04 AM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

This function in stdlib would fill the gap. Or we need an extra protocol GenericHashable which includes !=.

--
Adrian Zubarev
Sent with Airmail
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Adrian Zubarev) #5

I wasn’t asking to get something like this now. I was curios and I don’t feel like curios question like this should be asked in the user mail-list. Maybe we need an extra mail-list for discussion and this will only remain for proposals and review, which will sort out such additional topics and don’t bother the review process.

After I posted the question I realized myself that the given example will only work for generics with a single inner type.

That’s why you’re totally right about the ability without specialization. :slight_smile:

The next problem you’re design, that it does not work with structs and enums.

···

--
Adrian Zubarev
Sent with Airmail

Am 18. Juli 2016 um 09:17:44, Charlie Monroe (charlie@charliemonroe.net) schrieb:

In general, Swift will need a way to point to a generic type without a particular specialization. Currently, you can't do this:

class APICall<T> { }

class CombinedCall: APICall<Bool> {
var calls: [APICall] // Error - no specialization
}

I've been currently working around this by:

class APICallBase {} // No generics
class APICall<T>: APICallBase {}
class CombinedCall: APICall<Bool> {
var calls: [APICallBase]
}

But it's definitely a pain to work with.

As asked by the core team several times now, we should defer any discussion on additional features until August.

On Jul 18, 2016, at 9:04 AM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

This is something additional, but I’m curios about how the community feels about it.

I recently come across the issue where conforming to Hashable wasn’t enough to thecke if two instances of the same generic type were equal.

I had additionally provide myself the != function.

public func !=<T, U>(lhs: SomeTypeName<T>, rhs: SomeTypeName<U>) -> Bool {
    return lhs.hashValue != rhs.hashValue
}
I wondered if Swift can ever get generic specialization like this:

public func !=<T : Hashable, U, V>(lhs: T<U>, rhs: T<V>) -> Bool {
    return !(lhs.hashValue == rhs.hashValue)
}
This function in stdlib would fill the gap. Or we need an extra protocol GenericHashable which includes !=.

--
Adrian Zubarev
Sent with Airmail

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


(L Mihalkovic) #6

It was TypeScript

Regards
(From mobile)

···

On Jul 18, 2016, at 9:48 PM, L. Mihalkovic <laurent.mihalkovic@gmail.com> wrote:

Regards
(From mobile)

On Jul 18, 2016, at 9:04 AM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

This is something additional, but I’m curios about how the community feels about it.

I recently come across the issue where conforming to Hashable wasn’t enough to thecke if two instances of the same generic type were equal.

I had additionally provide myself the != function.

public func !=<T, U>(lhs: SomeTypeName<T>, rhs: SomeTypeName<U>) -> Bool {
    return lhs.hashValue != rhs.hashValue
}
I wondered if Swift can ever get generic specialization like this:

public func !=<T : Hashable, U, V>(lhs: T<U>, rhs: T<V>) -> Bool {
    return !(lhs.hashValue == rhs.hashValue)
}

I am not sure about the ordering... Interestingly enough, this is real code written last week as part of a framework:

Export function newSataStore<T extends DataStore<U>, U> (ctor: { new (config:DataStoreConfig):T, config:DataStoreConfig):T {
   return new ctor(config)
}

And this is legal code just to try:

interface Hashable {
  getHash(): number
}
interface T extends Hashable { }
class SomeTypeName<Type extends Hashable> {
  public getHash():number {
    return ...;
  }
  public type():this {
    return Type;
  }
}
function compare<T1 extends T, T2 extends T>(lhs:SomeTypeName<T1>, rhs:SomeTypeName<T2>):boolean {
  return lhs.getHash() != rhs.getHash();
}

but this is more interesting:

type List<T> = T & { next: List<T> } // listObj.next.next.next.???

This function in stdlib would fill the gap. Or we need an extra protocol GenericHashable which includes !=.

--
Adrian Zubarev
Sent with Airmail
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution