Operator ++= on a pair in Scala
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:
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
add a comment |
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:
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
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 withA
andAA
.
– Frank
Nov 12 '18 at 1:47
Your first snippet withx: (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 ofGrowable[A]
(actually I haveA
=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
add a comment |
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:
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
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:
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
scala generics
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 withA
andAA
.
– Frank
Nov 12 '18 at 1:47
Your first snippet withx: (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 ofGrowable[A]
(actually I haveA
=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
add a comment |
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 withA
andAA
.
– Frank
Nov 12 '18 at 1:47
Your first snippet withx: (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 ofGrowable[A]
(actually I haveA
=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
add a comment |
1 Answer
1
active
oldest
votes
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)
Thanks! Bonus question: is it possible to generalize by abstracting out++=
, so that we can automatically pass any (operation that takes aGrowable
and aTraversable
to aGrowable
) to (an operation that takes a pair ofGrowable
, a pair ofTraversable
and returns a pair ofGrowable
)? I'm lazy and don't want to write adef xyz
for every functionxyz
ofGrowable
:-)
– 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 anAppender1
withA
only andAppender2
with typesA
andB
, both with a pair on the LHS but with a single collection on the RHS forAppender1
for++=
, with the semantic that++=
adds the same things to both elements of the pair forAppender1
. It seemed to get confused about++=
in that case.
– Frank
Nov 12 '18 at 5:15
add a comment |
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
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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)
Thanks! Bonus question: is it possible to generalize by abstracting out++=
, so that we can automatically pass any (operation that takes aGrowable
and aTraversable
to aGrowable
) to (an operation that takes a pair ofGrowable
, a pair ofTraversable
and returns a pair ofGrowable
)? I'm lazy and don't want to write adef xyz
for every functionxyz
ofGrowable
:-)
– 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 anAppender1
withA
only andAppender2
with typesA
andB
, both with a pair on the LHS but with a single collection on the RHS forAppender1
for++=
, with the semantic that++=
adds the same things to both elements of the pair forAppender1
. It seemed to get confused about++=
in that case.
– Frank
Nov 12 '18 at 5:15
add a comment |
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)
Thanks! Bonus question: is it possible to generalize by abstracting out++=
, so that we can automatically pass any (operation that takes aGrowable
and aTraversable
to aGrowable
) to (an operation that takes a pair ofGrowable
, a pair ofTraversable
and returns a pair ofGrowable
)? I'm lazy and don't want to write adef xyz
for every functionxyz
ofGrowable
:-)
– 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 anAppender1
withA
only andAppender2
with typesA
andB
, both with a pair on the LHS but with a single collection on the RHS forAppender1
for++=
, with the semantic that++=
adds the same things to both elements of the pair forAppender1
. It seemed to get confused about++=
in that case.
– Frank
Nov 12 '18 at 5:15
add a comment |
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)
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)
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 aGrowable
and aTraversable
to aGrowable
) to (an operation that takes a pair ofGrowable
, a pair ofTraversable
and returns a pair ofGrowable
)? I'm lazy and don't want to write adef xyz
for every functionxyz
ofGrowable
:-)
– 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 anAppender1
withA
only andAppender2
with typesA
andB
, both with a pair on the LHS but with a single collection on the RHS forAppender1
for++=
, with the semantic that++=
adds the same things to both elements of the pair forAppender1
. It seemed to get confused about++=
in that case.
– Frank
Nov 12 '18 at 5:15
add a comment |
Thanks! Bonus question: is it possible to generalize by abstracting out++=
, so that we can automatically pass any (operation that takes aGrowable
and aTraversable
to aGrowable
) to (an operation that takes a pair ofGrowable
, a pair ofTraversable
and returns a pair ofGrowable
)? I'm lazy and don't want to write adef xyz
for every functionxyz
ofGrowable
:-)
– 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 anAppender1
withA
only andAppender2
with typesA
andB
, both with a pair on the LHS but with a single collection on the RHS forAppender1
for++=
, with the semantic that++=
adds the same things to both elements of the pair forAppender1
. 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
add a comment |
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.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
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 withA
andAA
.– 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 haveA
=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