Operator ++= on a pair in Scala










1














I have written the following in Scala:



implicit class Appender[A, B](x: (Growable[A], Growable[B])) 
def ++=(y: (TraversableOnce[A], TraversableOnce[B])): (Growable[A], Growable[B]) =
(x._1 ++= y._1, x._2 ++= y._2)



But Scala doesn't accept x._2 ++= y._2, complaining that: Expression of type Growable.this.type doesn't conform to expected type Growable[B].



I also tried:



enter image description here



for which IntelliJ suggests a return type of (Growable.this.type, Growable.this.type), which leads to other problems.



What am I missing? What would be the correct way to add this ++= operation to a Tuple2 of 2 Growable?



Is there a way to write that kind of "lifting" but generalizing it to the operation (here ++=) too? That would be some implicit Lift that can transfer an operation from the type inside a pair to the pair type itself. Is that possible in Scala?










share|improve this question























  • Try compiling with Scala compiler. It's just the type checker in the IDE sometimes doesn't understand perfectly normal Scala code.
    – Kolmar
    Nov 12 '18 at 1:37










  • It seems genuine (ie not just in IntelliJ) - the compiler refuses if I try to return (AA, AA) in the example with A and AA.
    – Frank
    Nov 12 '18 at 1:47











  • Your first snippet with x: (Growable[A], Growable[B]) compiles and works fine for me.
    – Kolmar
    Nov 12 '18 at 2:13










  • Not for me, because I have a specific subtype of Growable[A] (actually I have A = B), and the returned type (Growable.this.type, Growable.this.type) is not automatically casted to my specific subtype.
    – Frank
    Nov 12 '18 at 2:22















1














I have written the following in Scala:



implicit class Appender[A, B](x: (Growable[A], Growable[B])) 
def ++=(y: (TraversableOnce[A], TraversableOnce[B])): (Growable[A], Growable[B]) =
(x._1 ++= y._1, x._2 ++= y._2)



But Scala doesn't accept x._2 ++= y._2, complaining that: Expression of type Growable.this.type doesn't conform to expected type Growable[B].



I also tried:



enter image description here



for which IntelliJ suggests a return type of (Growable.this.type, Growable.this.type), which leads to other problems.



What am I missing? What would be the correct way to add this ++= operation to a Tuple2 of 2 Growable?



Is there a way to write that kind of "lifting" but generalizing it to the operation (here ++=) too? That would be some implicit Lift that can transfer an operation from the type inside a pair to the pair type itself. Is that possible in Scala?










share|improve this question























  • Try compiling with Scala compiler. It's just the type checker in the IDE sometimes doesn't understand perfectly normal Scala code.
    – Kolmar
    Nov 12 '18 at 1:37










  • It seems genuine (ie not just in IntelliJ) - the compiler refuses if I try to return (AA, AA) in the example with A and AA.
    – Frank
    Nov 12 '18 at 1:47











  • Your first snippet with x: (Growable[A], Growable[B]) compiles and works fine for me.
    – Kolmar
    Nov 12 '18 at 2:13










  • Not for me, because I have a specific subtype of Growable[A] (actually I have A = B), and the returned type (Growable.this.type, Growable.this.type) is not automatically casted to my specific subtype.
    – Frank
    Nov 12 '18 at 2:22













1












1








1







I have written the following in Scala:



implicit class Appender[A, B](x: (Growable[A], Growable[B])) 
def ++=(y: (TraversableOnce[A], TraversableOnce[B])): (Growable[A], Growable[B]) =
(x._1 ++= y._1, x._2 ++= y._2)



But Scala doesn't accept x._2 ++= y._2, complaining that: Expression of type Growable.this.type doesn't conform to expected type Growable[B].



I also tried:



enter image description here



for which IntelliJ suggests a return type of (Growable.this.type, Growable.this.type), which leads to other problems.



What am I missing? What would be the correct way to add this ++= operation to a Tuple2 of 2 Growable?



Is there a way to write that kind of "lifting" but generalizing it to the operation (here ++=) too? That would be some implicit Lift that can transfer an operation from the type inside a pair to the pair type itself. Is that possible in Scala?










share|improve this question















I have written the following in Scala:



implicit class Appender[A, B](x: (Growable[A], Growable[B])) 
def ++=(y: (TraversableOnce[A], TraversableOnce[B])): (Growable[A], Growable[B]) =
(x._1 ++= y._1, x._2 ++= y._2)



But Scala doesn't accept x._2 ++= y._2, complaining that: Expression of type Growable.this.type doesn't conform to expected type Growable[B].



I also tried:



enter image description here



for which IntelliJ suggests a return type of (Growable.this.type, Growable.this.type), which leads to other problems.



What am I missing? What would be the correct way to add this ++= operation to a Tuple2 of 2 Growable?



Is there a way to write that kind of "lifting" but generalizing it to the operation (here ++=) too? That would be some implicit Lift that can transfer an operation from the type inside a pair to the pair type itself. Is that possible in Scala?







scala generics






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 12 '18 at 1:51

























asked Nov 12 '18 at 1:14









Frank

1,93052846




1,93052846











  • Try compiling with Scala compiler. It's just the type checker in the IDE sometimes doesn't understand perfectly normal Scala code.
    – Kolmar
    Nov 12 '18 at 1:37










  • It seems genuine (ie not just in IntelliJ) - the compiler refuses if I try to return (AA, AA) in the example with A and AA.
    – Frank
    Nov 12 '18 at 1:47











  • Your first snippet with x: (Growable[A], Growable[B]) compiles and works fine for me.
    – Kolmar
    Nov 12 '18 at 2:13










  • Not for me, because I have a specific subtype of Growable[A] (actually I have A = B), and the returned type (Growable.this.type, Growable.this.type) is not automatically casted to my specific subtype.
    – Frank
    Nov 12 '18 at 2:22
















  • Try compiling with Scala compiler. It's just the type checker in the IDE sometimes doesn't understand perfectly normal Scala code.
    – Kolmar
    Nov 12 '18 at 1:37










  • It seems genuine (ie not just in IntelliJ) - the compiler refuses if I try to return (AA, AA) in the example with A and AA.
    – Frank
    Nov 12 '18 at 1:47











  • Your first snippet with x: (Growable[A], Growable[B]) compiles and works fine for me.
    – Kolmar
    Nov 12 '18 at 2:13










  • Not for me, because I have a specific subtype of Growable[A] (actually I have A = B), and the returned type (Growable.this.type, Growable.this.type) is not automatically casted to my specific subtype.
    – Frank
    Nov 12 '18 at 2:22















Try compiling with Scala compiler. It's just the type checker in the IDE sometimes doesn't understand perfectly normal Scala code.
– Kolmar
Nov 12 '18 at 1:37




Try compiling with Scala compiler. It's just the type checker in the IDE sometimes doesn't understand perfectly normal Scala code.
– Kolmar
Nov 12 '18 at 1:37












It seems genuine (ie not just in IntelliJ) - the compiler refuses if I try to return (AA, AA) in the example with A and AA.
– Frank
Nov 12 '18 at 1:47





It seems genuine (ie not just in IntelliJ) - the compiler refuses if I try to return (AA, AA) in the example with A and AA.
– Frank
Nov 12 '18 at 1:47













Your first snippet with x: (Growable[A], Growable[B]) compiles and works fine for me.
– Kolmar
Nov 12 '18 at 2:13




Your first snippet with x: (Growable[A], Growable[B]) compiles and works fine for me.
– Kolmar
Nov 12 '18 at 2:13












Not for me, because I have a specific subtype of Growable[A] (actually I have A = B), and the returned type (Growable.this.type, Growable.this.type) is not automatically casted to my specific subtype.
– Frank
Nov 12 '18 at 2:22




Not for me, because I have a specific subtype of Growable[A] (actually I have A = B), and the returned type (Growable.this.type, Growable.this.type) is not automatically casted to my specific subtype.
– Frank
Nov 12 '18 at 2:22












1 Answer
1






active

oldest

votes


















2














Your first snippet compiles and works fine for me, although IntelliJ underlines it.



The problem with this code though is that it loses information about the concrete types in the first pair. The result comes out as the opaque (Growable[A], Growable[B]).



To preserve the types you can use something similar to the second snippet. The problem is that the compiler seems unable to apply the implicit conversion to the pair of Growable subtypes. But if you try to create the Appender explicitly it works:



object Test 
implicit class Appender[
A, GA <: Growable[A],
B, GB <: Growable[B]
](x: (GA, GB))
def ++=(y: (TraversableOnce[A], TraversableOnce[B])): (GA, GB) =
(x._1 ++= y._1, x._2 ++= y._2)



def main(args: Array[String]): Unit =
val a = (ArrayBuffer(1,2,3), ArrayBuffer("a","b","c"))
println(new Appender(a).++=((List(4,5,6), List("d","e","f"))))




You can assist the compiler by defining the parts of x with a combined type GA with Growable[A]. In that case the compiler detects and applies the implicit conversion correctly using the Growable[A] part of the declaration. And you still have access to the full type GA to return from the method:



object Test 
implicit class Appender[
A, GA <: Growable[A],
B, GB <: Growable[B]
](x: (GA with Growable[A], GB with Growable[B]))
def ++=(y: (TraversableOnce[A], TraversableOnce[B])): (GA, GB) =
(x._1 ++= y._1, x._2 ++= y._2)



def main(args: Array[String]): Unit =
val a = (ArrayBuffer(1,2,3), ArrayBuffer("a","b","c"))
println(a ++= (List(4,5,6), List("d","e","f")))




Append:



As for your bonus question, I don't think there is a simple way to automate the creation of all methods. Maybe you can do something with macros, but I'm not sure. But I believe you can considerably simplify method definitions using some helper methods. There are not that many methods you can meaningfully define for Growable, so here is what I believe a full definition:



implicit class Appender[
A, GA <: Growable[A]
](x: (GA with Growable[A], GA with Growable[A]))
private def wrap[B, R](f: GA => B => R): ((B, B)) => (R, R) =
y => (f(x._1)(y._1), f(x._2)(y._2))

val ++= = wrap(_.++=)
val += = wrap(_.+=)

def clear(): Unit =
x._1.clear()
x._2.clear()




If you have some other operation



def myOpDef[A, GA <: Growable[A]](ga: GA, traversable: TraversableOnce[A]): GA = ???


you can also add it to the Appender definition (but it seems a bit tricky):



val myOp = wrap(ga => myOpDef(ga, _))


or



val myOp = wrap(myOpDef[A, GA].curried)





share|improve this answer






















  • Thanks! Bonus question: is it possible to generalize by abstracting out ++=, so that we can automatically pass any (operation that takes a Growable and a Traversable to a Growable) to (an operation that takes a pair of Growable, a pair of Traversable and returns a pair of Growable)? I'm lazy and don't want to write a def xyz for every function xyz of Growable :-)
    – Frank
    Nov 12 '18 at 2:46











  • I confirmed that your second solution works for me too. It is my choice: it seems a bit more elegant than the first one, although we shouldn't have to "assist the compiler", IMHO.
    – Frank
    Nov 12 '18 at 2:52











  • @Frank See if my edit answers your question. Also, I agree that we shouldn't have to assist the compiler, but I have an impression that those kinds of things are really non-trivial and may be approaching the limit of what is feasible to do with Scala's type system, which has to support OOP with subclasses, but also FP and all kinds of implicit resolution. I maybe wrong with that opinion though, and it's just an oversight.
    – Kolmar
    Nov 12 '18 at 3:46










  • thanks. The wrappers are not working in my context yet, but I'll continue working on it. I algo got in trouble when I defined an Appender1 with A only and Appender2 with types A and B, both with a pair on the LHS but with a single collection on the RHS for Appender1 for ++=, with the semantic that ++= adds the same things to both elements of the pair for Appender1. It seemed to get confused about ++= in that case.
    – Frank
    Nov 12 '18 at 5:15










Your Answer






StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);













draft saved

draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53254858%2foperator-on-a-pair-in-scala%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes









2














Your first snippet compiles and works fine for me, although IntelliJ underlines it.



The problem with this code though is that it loses information about the concrete types in the first pair. The result comes out as the opaque (Growable[A], Growable[B]).



To preserve the types you can use something similar to the second snippet. The problem is that the compiler seems unable to apply the implicit conversion to the pair of Growable subtypes. But if you try to create the Appender explicitly it works:



object Test 
implicit class Appender[
A, GA <: Growable[A],
B, GB <: Growable[B]
](x: (GA, GB))
def ++=(y: (TraversableOnce[A], TraversableOnce[B])): (GA, GB) =
(x._1 ++= y._1, x._2 ++= y._2)



def main(args: Array[String]): Unit =
val a = (ArrayBuffer(1,2,3), ArrayBuffer("a","b","c"))
println(new Appender(a).++=((List(4,5,6), List("d","e","f"))))




You can assist the compiler by defining the parts of x with a combined type GA with Growable[A]. In that case the compiler detects and applies the implicit conversion correctly using the Growable[A] part of the declaration. And you still have access to the full type GA to return from the method:



object Test 
implicit class Appender[
A, GA <: Growable[A],
B, GB <: Growable[B]
](x: (GA with Growable[A], GB with Growable[B]))
def ++=(y: (TraversableOnce[A], TraversableOnce[B])): (GA, GB) =
(x._1 ++= y._1, x._2 ++= y._2)



def main(args: Array[String]): Unit =
val a = (ArrayBuffer(1,2,3), ArrayBuffer("a","b","c"))
println(a ++= (List(4,5,6), List("d","e","f")))




Append:



As for your bonus question, I don't think there is a simple way to automate the creation of all methods. Maybe you can do something with macros, but I'm not sure. But I believe you can considerably simplify method definitions using some helper methods. There are not that many methods you can meaningfully define for Growable, so here is what I believe a full definition:



implicit class Appender[
A, GA <: Growable[A]
](x: (GA with Growable[A], GA with Growable[A]))
private def wrap[B, R](f: GA => B => R): ((B, B)) => (R, R) =
y => (f(x._1)(y._1), f(x._2)(y._2))

val ++= = wrap(_.++=)
val += = wrap(_.+=)

def clear(): Unit =
x._1.clear()
x._2.clear()




If you have some other operation



def myOpDef[A, GA <: Growable[A]](ga: GA, traversable: TraversableOnce[A]): GA = ???


you can also add it to the Appender definition (but it seems a bit tricky):



val myOp = wrap(ga => myOpDef(ga, _))


or



val myOp = wrap(myOpDef[A, GA].curried)





share|improve this answer






















  • Thanks! Bonus question: is it possible to generalize by abstracting out ++=, so that we can automatically pass any (operation that takes a Growable and a Traversable to a Growable) to (an operation that takes a pair of Growable, a pair of Traversable and returns a pair of Growable)? I'm lazy and don't want to write a def xyz for every function xyz of Growable :-)
    – Frank
    Nov 12 '18 at 2:46











  • I confirmed that your second solution works for me too. It is my choice: it seems a bit more elegant than the first one, although we shouldn't have to "assist the compiler", IMHO.
    – Frank
    Nov 12 '18 at 2:52











  • @Frank See if my edit answers your question. Also, I agree that we shouldn't have to assist the compiler, but I have an impression that those kinds of things are really non-trivial and may be approaching the limit of what is feasible to do with Scala's type system, which has to support OOP with subclasses, but also FP and all kinds of implicit resolution. I maybe wrong with that opinion though, and it's just an oversight.
    – Kolmar
    Nov 12 '18 at 3:46










  • thanks. The wrappers are not working in my context yet, but I'll continue working on it. I algo got in trouble when I defined an Appender1 with A only and Appender2 with types A and B, both with a pair on the LHS but with a single collection on the RHS for Appender1 for ++=, with the semantic that ++= adds the same things to both elements of the pair for Appender1. It seemed to get confused about ++= in that case.
    – Frank
    Nov 12 '18 at 5:15















2














Your first snippet compiles and works fine for me, although IntelliJ underlines it.



The problem with this code though is that it loses information about the concrete types in the first pair. The result comes out as the opaque (Growable[A], Growable[B]).



To preserve the types you can use something similar to the second snippet. The problem is that the compiler seems unable to apply the implicit conversion to the pair of Growable subtypes. But if you try to create the Appender explicitly it works:



object Test 
implicit class Appender[
A, GA <: Growable[A],
B, GB <: Growable[B]
](x: (GA, GB))
def ++=(y: (TraversableOnce[A], TraversableOnce[B])): (GA, GB) =
(x._1 ++= y._1, x._2 ++= y._2)



def main(args: Array[String]): Unit =
val a = (ArrayBuffer(1,2,3), ArrayBuffer("a","b","c"))
println(new Appender(a).++=((List(4,5,6), List("d","e","f"))))




You can assist the compiler by defining the parts of x with a combined type GA with Growable[A]. In that case the compiler detects and applies the implicit conversion correctly using the Growable[A] part of the declaration. And you still have access to the full type GA to return from the method:



object Test 
implicit class Appender[
A, GA <: Growable[A],
B, GB <: Growable[B]
](x: (GA with Growable[A], GB with Growable[B]))
def ++=(y: (TraversableOnce[A], TraversableOnce[B])): (GA, GB) =
(x._1 ++= y._1, x._2 ++= y._2)



def main(args: Array[String]): Unit =
val a = (ArrayBuffer(1,2,3), ArrayBuffer("a","b","c"))
println(a ++= (List(4,5,6), List("d","e","f")))




Append:



As for your bonus question, I don't think there is a simple way to automate the creation of all methods. Maybe you can do something with macros, but I'm not sure. But I believe you can considerably simplify method definitions using some helper methods. There are not that many methods you can meaningfully define for Growable, so here is what I believe a full definition:



implicit class Appender[
A, GA <: Growable[A]
](x: (GA with Growable[A], GA with Growable[A]))
private def wrap[B, R](f: GA => B => R): ((B, B)) => (R, R) =
y => (f(x._1)(y._1), f(x._2)(y._2))

val ++= = wrap(_.++=)
val += = wrap(_.+=)

def clear(): Unit =
x._1.clear()
x._2.clear()




If you have some other operation



def myOpDef[A, GA <: Growable[A]](ga: GA, traversable: TraversableOnce[A]): GA = ???


you can also add it to the Appender definition (but it seems a bit tricky):



val myOp = wrap(ga => myOpDef(ga, _))


or



val myOp = wrap(myOpDef[A, GA].curried)





share|improve this answer






















  • Thanks! Bonus question: is it possible to generalize by abstracting out ++=, so that we can automatically pass any (operation that takes a Growable and a Traversable to a Growable) to (an operation that takes a pair of Growable, a pair of Traversable and returns a pair of Growable)? I'm lazy and don't want to write a def xyz for every function xyz of Growable :-)
    – Frank
    Nov 12 '18 at 2:46











  • I confirmed that your second solution works for me too. It is my choice: it seems a bit more elegant than the first one, although we shouldn't have to "assist the compiler", IMHO.
    – Frank
    Nov 12 '18 at 2:52











  • @Frank See if my edit answers your question. Also, I agree that we shouldn't have to assist the compiler, but I have an impression that those kinds of things are really non-trivial and may be approaching the limit of what is feasible to do with Scala's type system, which has to support OOP with subclasses, but also FP and all kinds of implicit resolution. I maybe wrong with that opinion though, and it's just an oversight.
    – Kolmar
    Nov 12 '18 at 3:46










  • thanks. The wrappers are not working in my context yet, but I'll continue working on it. I algo got in trouble when I defined an Appender1 with A only and Appender2 with types A and B, both with a pair on the LHS but with a single collection on the RHS for Appender1 for ++=, with the semantic that ++= adds the same things to both elements of the pair for Appender1. It seemed to get confused about ++= in that case.
    – Frank
    Nov 12 '18 at 5:15













2












2








2






Your first snippet compiles and works fine for me, although IntelliJ underlines it.



The problem with this code though is that it loses information about the concrete types in the first pair. The result comes out as the opaque (Growable[A], Growable[B]).



To preserve the types you can use something similar to the second snippet. The problem is that the compiler seems unable to apply the implicit conversion to the pair of Growable subtypes. But if you try to create the Appender explicitly it works:



object Test 
implicit class Appender[
A, GA <: Growable[A],
B, GB <: Growable[B]
](x: (GA, GB))
def ++=(y: (TraversableOnce[A], TraversableOnce[B])): (GA, GB) =
(x._1 ++= y._1, x._2 ++= y._2)



def main(args: Array[String]): Unit =
val a = (ArrayBuffer(1,2,3), ArrayBuffer("a","b","c"))
println(new Appender(a).++=((List(4,5,6), List("d","e","f"))))




You can assist the compiler by defining the parts of x with a combined type GA with Growable[A]. In that case the compiler detects and applies the implicit conversion correctly using the Growable[A] part of the declaration. And you still have access to the full type GA to return from the method:



object Test 
implicit class Appender[
A, GA <: Growable[A],
B, GB <: Growable[B]
](x: (GA with Growable[A], GB with Growable[B]))
def ++=(y: (TraversableOnce[A], TraversableOnce[B])): (GA, GB) =
(x._1 ++= y._1, x._2 ++= y._2)



def main(args: Array[String]): Unit =
val a = (ArrayBuffer(1,2,3), ArrayBuffer("a","b","c"))
println(a ++= (List(4,5,6), List("d","e","f")))




Append:



As for your bonus question, I don't think there is a simple way to automate the creation of all methods. Maybe you can do something with macros, but I'm not sure. But I believe you can considerably simplify method definitions using some helper methods. There are not that many methods you can meaningfully define for Growable, so here is what I believe a full definition:



implicit class Appender[
A, GA <: Growable[A]
](x: (GA with Growable[A], GA with Growable[A]))
private def wrap[B, R](f: GA => B => R): ((B, B)) => (R, R) =
y => (f(x._1)(y._1), f(x._2)(y._2))

val ++= = wrap(_.++=)
val += = wrap(_.+=)

def clear(): Unit =
x._1.clear()
x._2.clear()




If you have some other operation



def myOpDef[A, GA <: Growable[A]](ga: GA, traversable: TraversableOnce[A]): GA = ???


you can also add it to the Appender definition (but it seems a bit tricky):



val myOp = wrap(ga => myOpDef(ga, _))


or



val myOp = wrap(myOpDef[A, GA].curried)





share|improve this answer














Your first snippet compiles and works fine for me, although IntelliJ underlines it.



The problem with this code though is that it loses information about the concrete types in the first pair. The result comes out as the opaque (Growable[A], Growable[B]).



To preserve the types you can use something similar to the second snippet. The problem is that the compiler seems unable to apply the implicit conversion to the pair of Growable subtypes. But if you try to create the Appender explicitly it works:



object Test 
implicit class Appender[
A, GA <: Growable[A],
B, GB <: Growable[B]
](x: (GA, GB))
def ++=(y: (TraversableOnce[A], TraversableOnce[B])): (GA, GB) =
(x._1 ++= y._1, x._2 ++= y._2)



def main(args: Array[String]): Unit =
val a = (ArrayBuffer(1,2,3), ArrayBuffer("a","b","c"))
println(new Appender(a).++=((List(4,5,6), List("d","e","f"))))




You can assist the compiler by defining the parts of x with a combined type GA with Growable[A]. In that case the compiler detects and applies the implicit conversion correctly using the Growable[A] part of the declaration. And you still have access to the full type GA to return from the method:



object Test 
implicit class Appender[
A, GA <: Growable[A],
B, GB <: Growable[B]
](x: (GA with Growable[A], GB with Growable[B]))
def ++=(y: (TraversableOnce[A], TraversableOnce[B])): (GA, GB) =
(x._1 ++= y._1, x._2 ++= y._2)



def main(args: Array[String]): Unit =
val a = (ArrayBuffer(1,2,3), ArrayBuffer("a","b","c"))
println(a ++= (List(4,5,6), List("d","e","f")))




Append:



As for your bonus question, I don't think there is a simple way to automate the creation of all methods. Maybe you can do something with macros, but I'm not sure. But I believe you can considerably simplify method definitions using some helper methods. There are not that many methods you can meaningfully define for Growable, so here is what I believe a full definition:



implicit class Appender[
A, GA <: Growable[A]
](x: (GA with Growable[A], GA with Growable[A]))
private def wrap[B, R](f: GA => B => R): ((B, B)) => (R, R) =
y => (f(x._1)(y._1), f(x._2)(y._2))

val ++= = wrap(_.++=)
val += = wrap(_.+=)

def clear(): Unit =
x._1.clear()
x._2.clear()




If you have some other operation



def myOpDef[A, GA <: Growable[A]](ga: GA, traversable: TraversableOnce[A]): GA = ???


you can also add it to the Appender definition (but it seems a bit tricky):



val myOp = wrap(ga => myOpDef(ga, _))


or



val myOp = wrap(myOpDef[A, GA].curried)






share|improve this answer














share|improve this answer



share|improve this answer








edited Nov 12 '18 at 3:41

























answered Nov 12 '18 at 2:43









Kolmar

11.4k11317




11.4k11317











  • Thanks! Bonus question: is it possible to generalize by abstracting out ++=, so that we can automatically pass any (operation that takes a Growable and a Traversable to a Growable) to (an operation that takes a pair of Growable, a pair of Traversable and returns a pair of Growable)? I'm lazy and don't want to write a def xyz for every function xyz of Growable :-)
    – Frank
    Nov 12 '18 at 2:46











  • I confirmed that your second solution works for me too. It is my choice: it seems a bit more elegant than the first one, although we shouldn't have to "assist the compiler", IMHO.
    – Frank
    Nov 12 '18 at 2:52











  • @Frank See if my edit answers your question. Also, I agree that we shouldn't have to assist the compiler, but I have an impression that those kinds of things are really non-trivial and may be approaching the limit of what is feasible to do with Scala's type system, which has to support OOP with subclasses, but also FP and all kinds of implicit resolution. I maybe wrong with that opinion though, and it's just an oversight.
    – Kolmar
    Nov 12 '18 at 3:46










  • thanks. The wrappers are not working in my context yet, but I'll continue working on it. I algo got in trouble when I defined an Appender1 with A only and Appender2 with types A and B, both with a pair on the LHS but with a single collection on the RHS for Appender1 for ++=, with the semantic that ++= adds the same things to both elements of the pair for Appender1. It seemed to get confused about ++= in that case.
    – Frank
    Nov 12 '18 at 5:15
















  • Thanks! Bonus question: is it possible to generalize by abstracting out ++=, so that we can automatically pass any (operation that takes a Growable and a Traversable to a Growable) to (an operation that takes a pair of Growable, a pair of Traversable and returns a pair of Growable)? I'm lazy and don't want to write a def xyz for every function xyz of Growable :-)
    – Frank
    Nov 12 '18 at 2:46











  • I confirmed that your second solution works for me too. It is my choice: it seems a bit more elegant than the first one, although we shouldn't have to "assist the compiler", IMHO.
    – Frank
    Nov 12 '18 at 2:52











  • @Frank See if my edit answers your question. Also, I agree that we shouldn't have to assist the compiler, but I have an impression that those kinds of things are really non-trivial and may be approaching the limit of what is feasible to do with Scala's type system, which has to support OOP with subclasses, but also FP and all kinds of implicit resolution. I maybe wrong with that opinion though, and it's just an oversight.
    – Kolmar
    Nov 12 '18 at 3:46










  • thanks. The wrappers are not working in my context yet, but I'll continue working on it. I algo got in trouble when I defined an Appender1 with A only and Appender2 with types A and B, both with a pair on the LHS but with a single collection on the RHS for Appender1 for ++=, with the semantic that ++= adds the same things to both elements of the pair for Appender1. It seemed to get confused about ++= in that case.
    – Frank
    Nov 12 '18 at 5:15















Thanks! Bonus question: is it possible to generalize by abstracting out ++=, so that we can automatically pass any (operation that takes a Growable and a Traversable to a Growable) to (an operation that takes a pair of Growable, a pair of Traversable and returns a pair of Growable)? I'm lazy and don't want to write a def xyz for every function xyz of Growable :-)
– Frank
Nov 12 '18 at 2:46





Thanks! Bonus question: is it possible to generalize by abstracting out ++=, so that we can automatically pass any (operation that takes a Growable and a Traversable to a Growable) to (an operation that takes a pair of Growable, a pair of Traversable and returns a pair of Growable)? I'm lazy and don't want to write a def xyz for every function xyz of Growable :-)
– Frank
Nov 12 '18 at 2:46













I confirmed that your second solution works for me too. It is my choice: it seems a bit more elegant than the first one, although we shouldn't have to "assist the compiler", IMHO.
– Frank
Nov 12 '18 at 2:52





I confirmed that your second solution works for me too. It is my choice: it seems a bit more elegant than the first one, although we shouldn't have to "assist the compiler", IMHO.
– Frank
Nov 12 '18 at 2:52













@Frank See if my edit answers your question. Also, I agree that we shouldn't have to assist the compiler, but I have an impression that those kinds of things are really non-trivial and may be approaching the limit of what is feasible to do with Scala's type system, which has to support OOP with subclasses, but also FP and all kinds of implicit resolution. I maybe wrong with that opinion though, and it's just an oversight.
– Kolmar
Nov 12 '18 at 3:46




@Frank See if my edit answers your question. Also, I agree that we shouldn't have to assist the compiler, but I have an impression that those kinds of things are really non-trivial and may be approaching the limit of what is feasible to do with Scala's type system, which has to support OOP with subclasses, but also FP and all kinds of implicit resolution. I maybe wrong with that opinion though, and it's just an oversight.
– Kolmar
Nov 12 '18 at 3:46












thanks. The wrappers are not working in my context yet, but I'll continue working on it. I algo got in trouble when I defined an Appender1 with A only and Appender2 with types A and B, both with a pair on the LHS but with a single collection on the RHS for Appender1 for ++=, with the semantic that ++= adds the same things to both elements of the pair for Appender1. It seemed to get confused about ++= in that case.
– Frank
Nov 12 '18 at 5:15




thanks. The wrappers are not working in my context yet, but I'll continue working on it. I algo got in trouble when I defined an Appender1 with A only and Appender2 with types A and B, both with a pair on the LHS but with a single collection on the RHS for Appender1 for ++=, with the semantic that ++= adds the same things to both elements of the pair for Appender1. It seemed to get confused about ++= in that case.
– Frank
Nov 12 '18 at 5:15

















draft saved

draft discarded
















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid


  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.





Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


Please pay close attention to the following guidance:


  • Please be sure to answer the question. Provide details and share your research!

But avoid


  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53254858%2foperator-on-a-pair-in-scala%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

How to how show current date and time by default on contact form 7 in WordPress without taking input from user in datetimepicker

Syphilis

Darth Vader #20