0

In particular, this minimal example:

trait A[T1, T2] {
  def convert(t1: T1): T2
  def reverse(t2: T2): T1
}

class B extends A[Int, Double]  {
  def convert(i: Int): Double = i.toDouble
  def reverse(i: Double): Int = i.toInt
}

class C extends A[Int, Float] {
  def convert(i: Int): Float = i.toFloat
  def reverse(i: Float): Int = i.toInt
}

val bOrC: A[Int, _] = if (System.nanoTime % 2 == 0) {
  new B
} else {
  new C
}

bOrC.convert(7)
bOrC.reverse(bOrC.convert(7))

Will fail on the last line:

scala> bOrC.reverse(bOrC.convert(7))
<console>:12: error: type mismatch;
 found   : (some other)_$1(in value bOrC)
 required: _$1(in value bOrC)
              bOrC.reverse(bOrC.convert(7))

It seems that the type of _$1 is the same in both the return type and the argument - it's bOrC's T2 type, whatever it may be. Local type inference shouldn't be a problem here. Why can't I do this?

Is there a workaround that isn't as ugly as the following?

trait A[T1, T2] {
  def convert(t1: T1): T2
  def reverse(t2: X): T1
  type X = T2
}
// ... rest as before
bOrC.reverse(bOrC.convert(7).asInstanceOf[bOrC.X])

Edit: looks like if you tell Scala it's the same type, everything is handled. This gets rid of the silly type X:

def thereAndBack[T2](a: A[Int, T2], b: Int) = a.reverse(a.convert(b))
thereAndBack(bOrC)
VF1
  • 1,594
  • 2
  • 11
  • 31
  • 1
    The underscore in this context isn't an "anonymous type", it's an _existential type_--which means you're telling the compiler you _don't care_ what the type is, so the compiler will happily treat it as such. – Michael Zajac Aug 29 '16 at 01:02
  • There a dissonance between your requirement of remembering the type and the usage of existential type, which does exactly the opposite of what you want. – Yuval Itzchakov Aug 29 '16 at 06:17
  • @m-z OK, yes, I don't care what type it is. But it is the same type being returned and used, whatever it is. Something seems missing there. – VF1 Aug 29 '16 at 21:36
  • @YuvalItzchakov I'd be happy to accept your answer, if you can show me why my question is ill-posed. – VF1 Aug 29 '16 at 21:39
  • @YuvalItzchakov it is a type forSome { type X }. It's that not we don't care about the type (and can forget it), it's that we don't care to name it. – som-snytt Aug 29 '16 at 21:56

1 Answers1

0

Perhaps you can treat A as a typeclass and only have one of B / C available implicitly?

def convert[T1, T2](x: T1)(implicit a: A[T1, T2]): T2 = {
  a.convert(x)
}

def reverse[T1, T2](x: T2)(implicit a: A[T1, T2]): T1 = {
  a.reverse(x)
}

implicit val b = new B
// or implicit val c = new C

convert(7)
reverse(convert(7))
Brian Kent
  • 3,754
  • 1
  • 26
  • 31
  • Thanks for the response, but I don't think this solution won't work. You're forcing a single code path to have one behavior, but this solution won't work if it's dependent on some runtime-determined value (such as `System.nanoTime` in my example). Also, it's not quite answering the _why_ question. – VF1 Aug 28 '16 at 22:29
  • I think why questions are disallowed; only how questions, IIUC. – som-snytt Aug 29 '16 at 00:14
  • @som-snytt That's a change from several questions I've asked before, and it hasn't been pointed out to me yet. Are you referencing something on the FAQ? – VF1 Aug 29 '16 at 21:34
  • @VF1 "why" usually invites opinions instead of solving a specific problem. here's a sample where I just tried to answer it anyway, and of course one winds up arguing in circles. http://stackoverflow.com/questions/39134978/why-cant-you-create-a-setter-without-getter-in-scala/39135701#39135701 – som-snytt Aug 29 '16 at 21:54
  • @som-snytt I agree that it is an invitation for opinions in general, but in this case, I think "why" can be a specific answer. E.g., the compiler doesn't have support for this / there's some crazy programming language theory that justifies this behavior / etc. would answer "why" here to satisfaction. – VF1 Aug 29 '16 at 22:00