1

Am trying to implicitly deconstruct a class instance into a tuple to create a nicer DSL syntax.

Here's a simplified example of what I'm trying to do:

class Pair[A,B](a: A, b: B){
  def left = a
  def right = b
}
val pair = new Pair(1,2)
implicit def unpair[T1 <: Int, T2 <: Int](p: Pair[T1,T2]) = {
  (p.left, p.right)
}
val(a,b) = pair

results in:

error: constructor cannot be instantiated to expected type;
 found   : (T1, T2)
 required: Pair[Int,Int]

I know I can define a companion object with unapply method and manually handle the deconstruction (or call above implicit explicitly), but that makes for unwanted boilerplate.

Edit Ok, just to provide a bit more context, Pair is embedded within an instance that implements map, flatMap, and withFilter (i.e. to be used within for comprehensions). So, desired usage looks something like:

val q = for{
  (a,b) <- tableA join tableB on(...)
  (c,d) <- tableC leftJoin tableD on(...)
  ....
} yield(a,b,c,d,...)

What I'd like to avoid is making Pair a case class (or adding a custom companion object to existing Pair class) and having to Pair(a,b) <- tableA join tableB on(...) everytime I join tables (read: often)

Original

Is there a way to pull this off in Scala 2.10 or 2.11?? There are some older SO threads from 2.8/2.9 days that indicate this functionality is not possible, but am hoping things have changed since then, or that there's a workaround available.

virtualeyes
  • 11,147
  • 6
  • 56
  • 91

2 Answers2

2

You need to set type of a and b explicit:

 val(a,b): (Int, Int) = pair
  • interesting, although in the context of the DSL that will wind up creating more boilerplate than manually unapplying. e.g. val(u,o): (Users,Orders) = pair, vs. val(u,o) = unpair(pair) – virtualeyes May 07 '14 at 08:23
  • So, i think you need to use companion object with unapply in this case. Cause val (a,b) ... it's pattern matching, and unapply was designed for that. In context of the DSL i think you need to operate with own domains and methods with more concrete type inference – Pavel Glushchenko May 07 '14 at 08:47
  • see updated question for actual use case. Probably right re: the companion object, was hoping an implicit could strip out the extra step... – virtualeyes May 07 '14 at 13:29
  • in updated case, try to set type of q :) Or if q returned by method, set return type of method. Actually if you want to use implicit conversions, i think you anyway need to set type of something. – Pavel Glushchenko May 07 '14 at 14:24
1

If you just want to extract left and right (I'm guessing from your example), why not to solve your problem with even less code:

case class Pair[A, B](left: A, right: B)

val pair = Pair(1, 2)

val Pair(a, b) = pair

println(a, b)
lpiepiora
  • 13,659
  • 1
  • 35
  • 47
  • right, that's what I was getting at with the companion object idea (Pair actually has several other fields, so it would look more like Pair(a,b,_,_,_,_) with case class' default companion object). In the context of the DSL Pair is contained within a wrapper that implements map, flatMap, and withFilter. So, within for comprehensions I'll have several Pairs that I want to unapply, and Pair(a,b) <- tableA join tableB on(...); Pair(c,d) <- tableC join tableD on(...), etc., isn't as elegant as an implicit Tuple2 destructuring – virtualeyes May 07 '14 at 12:40