'T != Type' in where clause


(Rex) #1

I often find myself running into situations where I'll receive "Ambiguous
use of..." for overloaded functions or operators. In every case these
situations would be easily solved if I could specify "Generic !=
CertainType" in the where clause of one of the overloads so I can
disambiguate the cases. Could this be added to language?

···

--

Rex Fenley | IOS DEVELOPER

Remind.com <https://www.remind.com/> | BLOG <http://blog.remind.com/>
> FOLLOW
US <https://twitter.com/remindhq> | LIKE US
<https://www.facebook.com/remindhq>


(David Sweeris) #2

+ all the 1s, along with something like "where !(T: Foo)"

IIRC, the topic has come up before, though I couldn't (quickly) find it and don't recall what the response was (other than some variation of "no", since we don't have it).

- Dave Sweeris

···

Sent from my iPhone

On Feb 27, 2017, at 16:34, Rex Fenley via swift-evolution <swift-evolution@swift.org> wrote:

I often find myself running into situations where I'll receive "Ambiguous use of..." for overloaded functions or operators. In every case these situations would be easily solved if I could specify "Generic != CertainType" in the where clause of one of the overloads so I can disambiguate the cases. Could this be added to language?


(Joe Groff) #3

Do you have a concrete example where you need this? It'd be good to know whether the types are ambiguous due to type checker bugs, or whether there's a principle by which they could be naturally ordered. Instead of overloading, can you do the type test via `if !(x is CertainType)` within a single implementation?

-Joe

···

On Feb 27, 2017, at 4:34 PM, Rex Fenley via swift-evolution <swift-evolution@swift.org> wrote:

I often find myself running into situations where I'll receive "Ambiguous use of..." for overloaded functions or operators. In every case these situations would be easily solved if I could specify "Generic != CertainType" in the where clause of one of the overloads so I can disambiguate the cases. Could this be added to language?


(Joe Groff) #4

This is an impossible constraint to enforce, since anyone can extend any type to conform to Foo.

-Joe

···

On Feb 27, 2017, at 10:21 PM, David Sweeris via swift-evolution <swift-evolution@swift.org> wrote:

Sent from my iPhone

On Feb 27, 2017, at 16:34, Rex Fenley via swift-evolution <swift-evolution@swift.org> wrote:

I often find myself running into situations where I'll receive "Ambiguous use of..." for overloaded functions or operators. In every case these situations would be easily solved if I could specify "Generic != CertainType" in the where clause of one of the overloads so I can disambiguate the cases. Could this be added to language?

+ all the 1s, along with something like "where !(T: Foo)"


(Matthew Johnson) #5

I often find myself running into situations where I'll receive "Ambiguous use of..." for overloaded functions or operators. In every case these situations would be easily solved if I could specify "Generic != CertainType" in the where clause of one of the overloads so I can disambiguate the cases. Could this be added to language?

Do you have a concrete example where you need this? It'd be good to know whether the types are ambiguous due to type checker bugs, or whether there's a principle by which they could be naturally ordered. Instead of overloading, can you do the type test via `if !(x is CertainType)` within a single implementation?

The best use case I can think of is if we had enum cases where the associated value is a subtype of the enum:

enum Result<T, E> where E: Error, T != E {
    case some(T) -> T
    case error(E) -> E
}

···

On Feb 28, 2017, at 11:04 AM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

On Feb 27, 2017, at 4:34 PM, Rex Fenley via swift-evolution <swift-evolution@swift.org> wrote:

-Joe

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


(Nicholas Maccharoli) #6

+ 1
I personally find this frustrating, but at the same time Im curious as to
what the argument against
introducing this is.

- Nick

···

On Tue, Feb 28, 2017 at 3:21 PM, David Sweeris via swift-evolution < swift-evolution@swift.org> wrote:

Sent from my iPhone
> On Feb 27, 2017, at 16:34, Rex Fenley via swift-evolution < > swift-evolution@swift.org> wrote:
>
> I often find myself running into situations where I'll receive
"Ambiguous use of..." for overloaded functions or operators. In every case
these situations would be easily solved if I could specify "Generic !=
CertainType" in the where clause of one of the overloads so I can
disambiguate the cases. Could this be added to language?

+ all the 1s, along with something like "where !(T: Foo)"

IIRC, the topic has come up before, though I couldn't (quickly) find it
and don't recall what the response was (other than some variation of "no",
since we don't have it).

- Dave Sweeris

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


(Nicolas Fezans) #7

+1
I would also welcome to be able to use "or" and "and" logical operators
(not only the not operator) on these constraints.
I have sometimes generic functions whose code is identical but is written
twice: first with 'where T=P1' and then with 'where T=P2', being able to
write for instance 'where T=(P1 or P2)' would be very handy IMO.
One could often argue that additional protocols and extensions could be
defined as a workaround to the situation I just mentioned but it seems
often a bit of an overkill to me when you only have a couple of functions
with that combination of requirements.

Nicolas

···

On Tue, Feb 28, 2017 at 7:53 AM, Nicholas Maccharoli via swift-evolution < swift-evolution@swift.org> wrote:

+ 1
I personally find this frustrating, but at the same time Im curious as to
what the argument against
introducing this is.

- Nick

On Tue, Feb 28, 2017 at 3:21 PM, David Sweeris via swift-evolution < > swift-evolution@swift.org> wrote:

Sent from my iPhone
> On Feb 27, 2017, at 16:34, Rex Fenley via swift-evolution < >> swift-evolution@swift.org> wrote:
>
> I often find myself running into situations where I'll receive
"Ambiguous use of..." for overloaded functions or operators. In every case
these situations would be easily solved if I could specify "Generic !=
CertainType" in the where clause of one of the overloads so I can
disambiguate the cases. Could this be added to language?

+ all the 1s, along with something like "where !(T: Foo)"

IIRC, the topic has come up before, though I couldn't (quickly) find it
and don't recall what the response was (other than some variation of "no",
since we don't have it).

- Dave Sweeris

_______________________________________________
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


(Joe Groff) #8

I don't think that's a good design for that type. I can see the desire for a subtype relationship between T and Result<T, E>, but no good reason for the error to also be a subtype. That != constraint would have to be propagated through anything using `Result<T, E>` as well.

-Joe

···

On Feb 28, 2017, at 9:23 AM, Matthew Johnson <matthew@anandabits.com> wrote:

On Feb 28, 2017, at 11:04 AM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

On Feb 27, 2017, at 4:34 PM, Rex Fenley via swift-evolution <swift-evolution@swift.org> wrote:

I often find myself running into situations where I'll receive "Ambiguous use of..." for overloaded functions or operators. In every case these situations would be easily solved if I could specify "Generic != CertainType" in the where clause of one of the overloads so I can disambiguate the cases. Could this be added to language?

Do you have a concrete example where you need this? It'd be good to know whether the types are ambiguous due to type checker bugs, or whether there's a principle by which they could be naturally ordered. Instead of overloading, can you do the type test via `if !(x is CertainType)` within a single implementation?

The best use case I can think of is if we had enum cases where the associated value is a subtype of the enum:

enum Result<T, E> where E: Error, T != E {
   case some(T) -> T
   case error(E) -> E
}


(Adrian Zubarev) #9

The or operator for types is rejected in Swift, and probably won’t ever make it, however there might be hope for enums to solve this issue. Some people pointed out in different threads how enums could mimic such behavior.

+1 for != type operator

I bumped myself once or twice into such a scenario, but I cannot remember what the use case was back then.

···

--
Adrian Zubarev
Sent with Airmail

Am 28. Februar 2017 um 08:22:07, Nicolas Fezans via swift-evolution (swift-evolution@swift.org) schrieb:

+1
I would also welcome to be able to use "or" and "and" logical operators (not only the not operator) on these constraints.
I have sometimes generic functions whose code is identical but is written twice: first with 'where T=P1' and then with 'where T=P2', being able to write for instance 'where T=(P1 or P2)' would be very handy IMO.
One could often argue that additional protocols and extensions could be defined as a workaround to the situation I just mentioned but it seems often a bit of an overkill to me when you only have a couple of functions with that combination of requirements.

Nicolas

On Tue, Feb 28, 2017 at 7:53 AM, Nicholas Maccharoli via swift-evolution <swift-evolution@swift.org> wrote:
+ 1
I personally find this frustrating, but at the same time Im curious as to what the argument against
introducing this is.

- Nick

On Tue, Feb 28, 2017 at 3:21 PM, David Sweeris via swift-evolution <swift-evolution@swift.org> wrote:

Sent from my iPhone

On Feb 27, 2017, at 16:34, Rex Fenley via swift-evolution <swift-evolution@swift.org> wrote:

I often find myself running into situations where I'll receive "Ambiguous use of..." for overloaded functions or operators. In every case these situations would be easily solved if I could specify "Generic != CertainType" in the where clause of one of the overloads so I can disambiguate the cases. Could this be added to language?

+ all the 1s, along with something like "where !(T: Foo)"

IIRC, the topic has come up before, though I couldn't (quickly) find it and don't recall what the response was (other than some variation of "no", since we don't have it).

- Dave Sweeris

_______________________________________________
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) #10

I often find myself running into situations where I'll receive "Ambiguous use of..." for overloaded functions or operators. In every case these situations would be easily solved if I could specify "Generic != CertainType" in the where clause of one of the overloads so I can disambiguate the cases. Could this be added to language?

Do you have a concrete example where you need this? It'd be good to know whether the types are ambiguous due to type checker bugs, or whether there's a principle by which they could be naturally ordered. Instead of overloading, can you do the type test via `if !(x is CertainType)` within a single implementation?

The best use case I can think of is if we had enum cases where the associated value is a subtype of the enum:

enum Result<T, E> where E: Error, T != E {
  case some(T) -> T
  case error(E) -> E
}

I don't think that's a good design for that type. I can see the desire for a subtype relationship between T and Result<T, E>, but no good reason for the error to also be a subtype. That != constraint would have to be propagated through anything using `Result<T, E>` as well.

Ok, just change it to a fully generic Either type then. I’m not arguing for or against this constraint, just pointing out a use case that is enabled by it. It’s reasonable to argue that we don’t want to support this use case.

···

On Feb 28, 2017, at 11:33 AM, Joe Groff <jgroff@apple.com> wrote:

On Feb 28, 2017, at 9:23 AM, Matthew Johnson <matthew@anandabits.com> wrote:

On Feb 28, 2017, at 11:04 AM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

On Feb 27, 2017, at 4:34 PM, Rex Fenley via swift-evolution <swift-evolution@swift.org> wrote:

-Joe


(Douglas Gregor) #11

+1
I would also welcome to be able to use "or" and "and" logical operators (not only the not operator) on these constraints.

You already have “and’ constraints: it’s what you get out of the comma-separated list of constraints in a where clause, or the “&” composition syntax.

I have sometimes generic functions whose code is identical but is written twice: first with 'where T=P1' and then with 'where T=P2', being able to write for instance 'where T=(P1 or P2)' would be very handy IMO.
One could often argue that additional protocols and extensions could be defined as a workaround to the situation I just mentioned but it seems often a bit of an overkill to me when you only have a couple of functions with that combination of requirements.

“Or” constraints are a nonstarter for me, because you can’t meaningfully type-check a generic function that uses “or” constraints: the problem goes exponential in the number of “or” constraints and the meaning of the function can change considerably depending on which set of terms are satisfied—in which case you have ambiguities again!

Whenever this topic comes up, I like to point people at:

  http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2161.pdf

The problems for Swift won’t be quite as extreme as they would have been for C++0x concepts—the *parsing* won’t have a totally different meaning—but all of the inferred types, selected overloads, etc. could all be completely different.

Note that the paper above talks about “not” constraints as well, and concludes that, effectively “they’re harmless and occasionally useful”, which I think also holds for Swift. However, I *personally* don’t believe that the problems they solve are significant enough to warrant inclusion in Swift 4, which already includes a pile of generics improvements. I suspect that the rest of the core team would agree with that assessment, but of course they’re free to weigh in themselves.

  - Doug

···

On Feb 27, 2017, at 11:21 PM, Nicolas Fezans via swift-evolution <swift-evolution@swift.org> wrote:

Nicolas

On Tue, Feb 28, 2017 at 7:53 AM, Nicholas Maccharoli via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
+ 1
I personally find this frustrating, but at the same time Im curious as to what the argument against
introducing this is.

- Nick

On Tue, Feb 28, 2017 at 3:21 PM, David Sweeris via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Sent from my iPhone
> On Feb 27, 2017, at 16:34, Rex Fenley via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>
> I often find myself running into situations where I'll receive "Ambiguous use of..." for overloaded functions or operators. In every case these situations would be easily solved if I could specify "Generic != CertainType" in the where clause of one of the overloads so I can disambiguate the cases. Could this be added to language?

+ all the 1s, along with something like "where !(T: Foo)"

IIRC, the topic has come up before, though I couldn't (quickly) find it and don't recall what the response was (other than some variation of "no", since we don't have it).

- Dave Sweeris

_______________________________________________
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


(David Hart) #12

+1
I would also welcome to be able to use "or" and "and" logical operators (not only the not operator) on these constraints.

You already have “and’ constraints: it’s what you get out of the comma-separated list of constraints in a where clause, or the “&” composition syntax.

I have sometimes generic functions whose code is identical but is written twice: first with 'where T=P1' and then with 'where T=P2', being able to write for instance 'where T=(P1 or P2)' would be very handy IMO.
One could often argue that additional protocols and extensions could be defined as a workaround to the situation I just mentioned but it seems often a bit of an overkill to me when you only have a couple of functions with that combination of requirements.

“Or” constraints are a nonstarter for me, because you can’t meaningfully type-check a generic function that uses “or” constraints: the problem goes exponential in the number of “or” constraints and the meaning of the function can change considerably depending on which set of terms are satisfied—in which case you have ambiguities again!

Whenever this topic comes up, I like to point people at:

  http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2161.pdf

Should we also follow Recommendation #2 and revert the P1 & P2 change to return to Any<P1, P2> :slight_smile: Half-joking.

···

On 28 Feb 2017, at 22:39, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

On Feb 27, 2017, at 11:21 PM, Nicolas Fezans via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

The problems for Swift won’t be quite as extreme as they would have been for C++0x concepts—the *parsing* won’t have a totally different meaning—but all of the inferred types, selected overloads, etc. could all be completely different.

Note that the paper above talks about “not” constraints as well, and concludes that, effectively “they’re harmless and occasionally useful”, which I think also holds for Swift. However, I *personally* don’t believe that the problems they solve are significant enough to warrant inclusion in Swift 4, which already includes a pile of generics improvements. I suspect that the rest of the core team would agree with that assessment, but of course they’re free to weigh in themselves.

  - Doug

Nicolas

On Tue, Feb 28, 2017 at 7:53 AM, Nicholas Maccharoli via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
+ 1
I personally find this frustrating, but at the same time Im curious as to what the argument against
introducing this is.

- Nick

On Tue, Feb 28, 2017 at 3:21 PM, David Sweeris via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Sent from my iPhone
> On Feb 27, 2017, at 16:34, Rex Fenley via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>
> I often find myself running into situations where I'll receive "Ambiguous use of..." for overloaded functions or operators. In every case these situations would be easily solved if I could specify "Generic != CertainType" in the where clause of one of the overloads so I can disambiguate the cases. Could this be added to language?

+ all the 1s, along with something like "where !(T: Foo)"

IIRC, the topic has come up before, though I couldn't (quickly) find it and don't recall what the response was (other than some variation of "no", since we don't have it).

- Dave Sweeris

_______________________________________________
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


(David Sweeris) #13

Only for protocols, right? I mean, as far as I know, you can’t declare a superclass in an extension.

I've been thinking about this for, well, about a day and a half, and I don't understand why it’s a problem. Wouldn’t any concrete type’s conformance propagate through the type system? How else would generic functions deal with types being extended outside the generic function’s module?

- Dave Sweeris

···

On Feb 28, 2017, at 09:01, Joe Groff <jgroff@apple.com> wrote:

On Feb 27, 2017, at 10:21 PM, David Sweeris via swift-evolution <swift-evolution@swift.org> wrote:

+ all the 1s, along with something like "where !(T: Foo)"

This is an impossible constraint to enforce, since anyone can extend any type to conform to Foo.


(Rex) #14

Here's a simplified section of code I'm writing for mapping some data from
over the wire

public protocol Transform {
    // a bunch of stuff
}

public protocol Key {
    // a bunch of stuff
}

extension String: Key { /* conforming stuff */ }

public protocol JSONType { }
extension Int: JSONType { }

public enum Bind<T>: Key {
    case transform(Key, T)
    // a few more cases, every case must have a Key, we forward all of
Key's methods through each case.
}

public func map<T: JSONType>(field: inout T, binding: Key) {
    print(field)
}

public func map<T, U: Transform>(field: inout T, binding: Bind<U>) {
    print(field)
}

class SpecialTransform: Transform { }

var s = Int()
// Ambiguity here.
map(field: &s, binding: Bind.transform("data.stuff", SpecialTransform()))

Probably would be better to make `Bind` not conform to `Key`, would just
require a bunch of refactoring in places where I've used the two
interchangeably. However, one of the benefits of having `Bind: Key` is that
it enforces that every additional case has a `Key` which will always be
true for this data structure.

I was thinking the first `map` would change to

public func map<T: JSONType, K: Key>(field: inout T, binding: K)

But to have `K != Bind<Any>`, Bind<Any> would have to be the super type of
all other Bind<T>s which currently isn't true anyway so this is kind of
dead on arrival.

Interestingly though, when I change the signature to

public func map<T: JSONType, K: Key>(field: inout T, binding: K)

Instead of "Ambiguous use of...", the program compiles and calls this
method instead of the overload with Bind<U>. Not only is this not the
function I want called, but this seems like inconsistent behavior with the
type checker (compiling vs throwing an "Ambiguous use of..." error).
Should I report a bug here or am I missing something?

···

On Tue, Feb 28, 2017 at 9:35 AM, Matthew Johnson <matthew@anandabits.com> wrote:

On Feb 28, 2017, at 11:33 AM, Joe Groff <jgroff@apple.com> wrote:

On Feb 28, 2017, at 9:23 AM, Matthew Johnson <matthew@anandabits.com> > wrote:

On Feb 28, 2017, at 11:04 AM, Joe Groff via swift-evolution < > swift-evolution@swift.org> wrote:

On Feb 27, 2017, at 4:34 PM, Rex Fenley via swift-evolution < > swift-evolution@swift.org> wrote:

I often find myself running into situations where I'll receive "Ambiguous
use of..." for overloaded functions or operators. In every case these
situations would be easily solved if I could specify "Generic !=
CertainType" in the where clause of one of the overloads so I can
disambiguate the cases. Could this be added to language?

Do you have a concrete example where you need this? It'd be good to know
whether the types are ambiguous due to type checker bugs, or whether
there's a principle by which they could be naturally ordered. Instead of
overloading, can you do the type test via `if !(x is CertainType)` within a
single implementation?

The best use case I can think of is if we had enum cases where the
associated value is a subtype of the enum:

enum Result<T, E> where E: Error, T != E {
  case some(T) -> T
  case error(E) -> E
}

I don't think that's a good design for that type. I can see the desire for
a subtype relationship between T and Result<T, E>, but no good reason for
the error to also be a subtype. That != constraint would have to be
propagated through anything using `Result<T, E>` as well.

Ok, just change it to a fully generic Either type then. I’m not arguing
for or against this constraint, just pointing out a use case that is
enabled by it. It’s reasonable to argue that we don’t want to support this
use case.

-Joe

--

Rex Fenley | IOS DEVELOPER

Remind.com <https://www.remind.com/> | BLOG <http://blog.remind.com/>
> FOLLOW
US <https://twitter.com/remindhq> | LIKE US
<https://www.facebook.com/remindhq>


(Douglas Gregor) #15

That line of argument got thoroughly shot down in the core team meeting when we discussed the introduction of the & operator for types.

  - Doug

···

On Feb 28, 2017, at 2:00 PM, David Hart <david@hartbit.com> wrote:

On 28 Feb 2017, at 22:39, Douglas Gregor via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Feb 27, 2017, at 11:21 PM, Nicolas Fezans via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

+1
I would also welcome to be able to use "or" and "and" logical operators (not only the not operator) on these constraints.

You already have “and’ constraints: it’s what you get out of the comma-separated list of constraints in a where clause, or the “&” composition syntax.

I have sometimes generic functions whose code is identical but is written twice: first with 'where T=P1' and then with 'where T=P2', being able to write for instance 'where T=(P1 or P2)' would be very handy IMO.
One could often argue that additional protocols and extensions could be defined as a workaround to the situation I just mentioned but it seems often a bit of an overkill to me when you only have a couple of functions with that combination of requirements.

“Or” constraints are a nonstarter for me, because you can’t meaningfully type-check a generic function that uses “or” constraints: the problem goes exponential in the number of “or” constraints and the meaning of the function can change considerably depending on which set of terms are satisfied—in which case you have ambiguities again!

Whenever this topic comes up, I like to point people at:

  http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2161.pdf

Should we also follow Recommendation #2 and revert the P1 & P2 change to return to Any<P1, P2> :slight_smile: Half-joking.


(Adrian Zubarev) #16

I also thought about the Either type.

enum Either<A, B> where A != B {
    case -> A
    case -> B
}

···

--
Adrian Zubarev
Sent with Airmail

Am 28. Februar 2017 um 18:35:45, Matthew Johnson via swift-evolution (swift-evolution@swift.org) schrieb:

On Feb 28, 2017, at 11:33 AM, Joe Groff <jgroff@apple.com> wrote:

On Feb 28, 2017, at 9:23 AM, Matthew Johnson <matthew@anandabits.com> wrote:

On Feb 28, 2017, at 11:04 AM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

On Feb 27, 2017, at 4:34 PM, Rex Fenley via swift-evolution <swift-evolution@swift.org> wrote:

I often find myself running into situations where I'll receive "Ambiguous use of..." for overloaded functions or operators. In every case these situations would be easily solved if I could specify "Generic != CertainType" in the where clause of one of the overloads so I can disambiguate the cases. Could this be added to language?

Do you have a concrete example where you need this? It'd be good to know whether the types are ambiguous due to type checker bugs, or whether there's a principle by which they could be naturally ordered. Instead of overloading, can you do the type test via `if !(x is CertainType)` within a single implementation?

The best use case I can think of is if we had enum cases where the associated value is a subtype of the enum:

enum Result<T, E> where E: Error, T != E {
case some(T) -> T
case error(E) -> E
}

I don't think that's a good design for that type. I can see the desire for a subtype relationship between T and Result<T, E>, but no good reason for the error to also be a subtype. That != constraint would have to be propagated through anything using `Result<T, E>` as well.

Ok, just change it to a fully generic Either type then. I’m not arguing for or against this constraint, just pointing out a use case that is enabled by it. It’s reasonable to argue that we don’t want to support this use case.

-Joe

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


(Jon Hull) #17

What I would like is a way to specialize a generic function in a block of code: (Strawman syntax)

  if T is Int.self {
    //In this block of code T == Int
  }

It seems to me that the compiler might even be able to optimize this without a branch. If there is a return in the block, then it might even be able to throw away all the code after it for the Int version of the function.

Here is some actual (super ugly) code I wrote today. I am unhappy with it and looking to remove or improve it, but it is the best I can figure out at the moment:

public static func operation<T>(symbol:String, type:T.Type)->((T,T)->T)?{
        if T.self == Int.self {
            var op:((Int,Int)->Int)? = nil
            switch symbol {
            case "+": op = (+)
            case "-": op = (-)
            case "*","x","•": op = (*)
            case "/","÷": op = (/)
            default: break
            }
            if op != nil {
                return op as! ((T,T)->T)?
            }
        }
// Function continues...

All this function does is take a string with a mathematical symbol in it and return an operation associated with that symbol/type (if it exists). In addition to the built in ones here, the user is able to register custom symbols and operations (the machinery for that is further down and less ugly).

Notice that I need to cast the operation back to (T,T)->T because it doesn’t know that T is an Int here. It has an error saying the cast always fails, but that isn’t true.

Ideally, I would be able to check if T is Numeric and then just return the operator. I have to do a bunch of extra work here to disambiguate for the compiler. It needs me to call out specific types (like Int) individually and then cast them back to T.

What I would like to do:

public static func operation<T>(symbol:String, type:T.Type)->((T,T)->T)?{
        if T.self is Numeric {
            switch symbol {
            case "+": return (+)
            case "-": return (-)
            case "*","x","•": return (*)
            case "/","÷": return (/)
            default: break
            }
        }
// Function continues…

or at least:

public static func operation<T>(symbol:String, type:T.Type)->((T,T)->T)?{
        if T.self is Int {
            switch symbol {
            case "+": return (+)
            case "-": return (-)
            case "*","x","•": return (*)
            case "/","÷": return (/)
            default: break
            }
        }
// Function continues...

Is it something that could be possible in a future version of Swift? or am I missing something obvious that is much better than this?

Thanks,
Jon

···

On Feb 28, 2017, at 2:15 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

On Feb 28, 2017, at 2:00 PM, David Hart <david@hartbit.com <mailto:david@hartbit.com>> wrote:

On 28 Feb 2017, at 22:39, Douglas Gregor via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Feb 27, 2017, at 11:21 PM, Nicolas Fezans via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

+1
I would also welcome to be able to use "or" and "and" logical operators (not only the not operator) on these constraints.

You already have “and’ constraints: it’s what you get out of the comma-separated list of constraints in a where clause, or the “&” composition syntax.

I have sometimes generic functions whose code is identical but is written twice: first with 'where T=P1' and then with 'where T=P2', being able to write for instance 'where T=(P1 or P2)' would be very handy IMO.
One could often argue that additional protocols and extensions could be defined as a workaround to the situation I just mentioned but it seems often a bit of an overkill to me when you only have a couple of functions with that combination of requirements.

“Or” constraints are a nonstarter for me, because you can’t meaningfully type-check a generic function that uses “or” constraints: the problem goes exponential in the number of “or” constraints and the meaning of the function can change considerably depending on which set of terms are satisfied—in which case you have ambiguities again!

Whenever this topic comes up, I like to point people at:

  http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2161.pdf

Should we also follow Recommendation #2 and revert the P1 & P2 change to return to Any<P1, P2> :slight_smile: Half-joking.

That line of argument got thoroughly shot down in the core team meeting when we discussed the introduction of the & operator for types.

  - Doug

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


(Joe Groff) #18

Protocol conformances are not intrinsic to the type in Swift. They're effectively independent parameters to generic contexts that require them. The caller from outside the generic function's model is responsible for choosing a conformance to pass. There's no way to statically enforce that no such conformance exists, and there's usually a better way to model things.

-Joe

···

On Mar 1, 2017, at 5:27 PM, David Sweeris <davesweeris@mac.com> wrote:

On Feb 28, 2017, at 09:01, Joe Groff <jgroff@apple.com> wrote:

On Feb 27, 2017, at 10:21 PM, David Sweeris via swift-evolution <swift-evolution@swift.org> wrote:

+ all the 1s, along with something like "where !(T: Foo)"

This is an impossible constraint to enforce, since anyone can extend any type to conform to Foo.

Only for protocols, right? I mean, as far as I know, you can’t declare a superclass in an extension.

I've been thinking about this for, well, about a day and a half, and I don't understand why it’s a problem. Wouldn’t any concrete type’s conformance propagate through the type system? How else would generic functions deal with types being extended outside the generic function’s module?


(Douglas Gregor) #19

We’ve been calling this “type refinement”. Essentially, within some lexical context (the “if” here) we can assert additional properties on a type or one specific (constant) values and take advantage of those properties, as you’ve done with T == Int.

It’s a plausible language feature, and could be useful. There’s certainly some precedent for it: C++17 adds something similar with “constexpr if”, albeit in their non-separately-type-checked template instantiation model.

It’s a nontrivial language feature that’s well out of scope for Swift 4.

  - Doug

···

On Mar 1, 2017, at 1:55 AM, Jonathan Hull <jhull@gbis.com> wrote:

What I would like is a way to specialize a generic function in a block of code: (Strawman syntax)

  if T is Int.self {
    //In this block of code T == Int
  }

It seems to me that the compiler might even be able to optimize this without a branch. If there is a return in the block, then it might even be able to throw away all the code after it for the Int version of the function.


(David Sweeris) #20

I was thinking about using it as a workaround for not having typed throws:
protocol MyErrorType: Error {...} // all my custom errors conform to this
enum MyError<T: MyErrorType, U: Error> : Error where !(U: FixableError) {
    case myError(T)
    case otherError(U)
}

Which ensures that, if an instance of "MyError" is .otherError, the payload will actually be an other error (or at least not an error that originated with any of my code).

Seems like if the program logic is intended to ensure that something is only a value of an exact type and not some sort of subtype, there ought to be a way to express that to the type system.

- Dave Sweeris

···

On Feb 28, 2017, at 09:38, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

I also thought about the Either type.

enum Either<A, B> where A != B {
    case -> A
    case -> B
}