Generics ambiguous argument

I am seeing a compiler error: Ambiguous use of init for a case that to me doesn't seem ambiguous. I have created a simplified code snippet that demonstrates the issue. Essentially, when using Generics and a pair of initializers with one and two arguments, passing two arguments gets interpreted as passing a tuple as the Generic element.


struct Point<Element> {
  var x : Element! = nil
  var y : Element! = nil

  init(_ x : Element) {
       self.x = x
  }

  init(_ x : Element, _ y : Element) {
       self.x = x
       self.y = y
  }
}
let p = Point(1, 2)     // this produces an Ambiguous use of init compile error


Obviously, I can resolve this by declaring the type for variable p, but the point is that the compiler actually thinks I might be passing tuple (1,2) as the argument. In fact, if I remove the second initializer, the code compiles fine and variable p is considered to have type: Point<(Int,Int> which would be equivalent if I had written instead:

struct Point<Element> {
  var x : Element! = nil
  var y : Element! = nil

  init(_ x : Element) {
       self.x = x
  }
}

// the following two are being treated as equivalent

let p1 = Point(1, 2)
let p2 = Point((1,2))


To me this is a compiler bug. The compiler should treat Point(1,2) strictly as taking two arguments since I could pass Point((1,2)) if I really wanted to t pass a tuple instead.


Is this a known compiler bug, or am I thinking about this the wrong way?

Replies

I definitely see where you are coming from, and I don't wish to say 'you are _wrong_' but maybe I can offer an alternative point of view.


I think the difficulty in thinking about this may come from having called the structure Point. Because we all have preconceived ideas about what a point is (after many a dreary hour in geometry class...) But, the compiler did not spend those hours in Geometry and doesn't know that a Point is an extra-special meaningful thing.


So, imagine if it was:


struct ICanHoldOneOrTwoOfAnything<Element> {

// and everything else is the same.

}


And the constructors make it possible to create an ICanHoldOneOrTwoOfAnything with, dare I say, one Element or two Elements. And amongst the candidates for the title of 'anything' is certainly Int and Float and struct and class and, yes, tuple. Because all those qualify as anything (ie Element).


As far as the syntax goes, it is true that there is some _thing_ wonderful about the relationship between function arguments and tuples. And whenever there is some feature that is wonderul, there is usually an accompanying edge situation where the behaviour appears weird. I'm pretty sure the Swift Language Reference Manual mentions that a functions arguments are a tuple, but I of course can't find the spot right now.


And naturally most languages have a few of these weird and wonderful things and the hope is the wonder outweighs the weird. In this particular case, I can understand how and why it works like it does, and I can see the advantage of defining a functions arguments to be a tuple, but I can also see how it can cause confusion when the weird aspect of it rears its ugly head.

Actually, in my real code, it is an Enum with associated types (one or two) instead of a Struct. I just used a struct to keep the example simple, but unfortunately I think it may have created confusion about my posting. Certainly, I wouldn't define a Point or even a Struct that way. That is why I qualified my original post as being a simplified code snippet.


I get that in many cases (not all) a function's arguments can be assigned as a Tuple, but that is different from that tuple being the argument. I don't think it is an either/or situation. In my code snippet, the tuple is being interpreted as the Generic argument. If instead it had been interpreted as the argument list, there would be no issue.

I don't dispute any of DTM's logic, but the question here is really about precedence and ambiguity. Of course tuples should be allowed as generic types, generally speaking. But in this case, they're just creating conflict where no reasonable person would expect it.


Swift allows "ambiguous" function overloading and resolves calls by prioritizing the candidates according to rules that, AFAIK, have never been formally documented but which are explored in some detail at AirSpeedVelocity.


Type inference is a separate domain, but my naive expectation would be that similar common-sense precedence rules ought to apply. If a set of type constraints can be satisfied by scalars, wouldn't it make sense to disregard the potential tuple interpretation?