Unpack tuple to var

435 views
Skip to first unread message

Radim Kolar

unread,
Jun 4, 2013, 7:06:57 PM6/4/13
to scala-language
proposed addition to scala language:

val t = (1,2,3)
var a,b,c = 10
( a,b,c ) = t

will do

a = 1
b = 2
c = 3

https://issues.scala-lang.org/browse/SI-7547

Aleksey Nikiforov

unread,
Jun 4, 2013, 8:05:12 PM6/4/13
to scala-l...@googlegroups.com
This would be useful. Unfortunately val from tuple is considered a special case of pattern matching, so it is unlikely to ever change. "val 1 = 2" comes to mind as an example of quantum entanglement between pattern matching and vals.




--
You received this message because you are subscribed to the Google Groups "scala-language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-language+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.



Radim Kolar

unread,
Jun 5, 2013, 3:11:34 AM6/5/13
to scala-l...@googlegroups.com
this seems to be often requested feature

https://issues.scala-lang.org/browse/SI-1324

I do not understand whats basic problem with this. Whats "syntax sugar
for updates" ?

Radim Kolar

unread,
Jun 5, 2013, 12:33:19 PM6/5/13
to scala-l...@googlegroups.com
this possible conflicting syntax sugar for updates do not compiles

import scala.collection.mutable._

class updatetest extends ListMap[String,Int] {

("two") = 2
}


and this:

class updatetest extends ListMap[String,Int]
object test {
val a = new updatetest()
a("one") = 1
}

is not in conflict with my proposal

Ryan Hendrickson

unread,
Jun 5, 2013, 12:44:54 PM6/5/13
to scala-l...@googlegroups.com
> and this:
>
> class updatetest extends ListMap[String,Int]
> object test {
> val a = new updatetest()
> a("one") = 1
> }
>
> is not in conflict with my proposal

It is if you assume that the thing to implement should not be limited to just the special case of tuples, but applies to pattern matching in general. Then

var b = "one"
a(b) = 1

could mean

a.update(b, 1)

or (approximately)

b = a.unapply(1).get






(please forgive me my corporate legal disclaimer)

----------------------------------------

This message is intended exclusively for the individual(s) or entity to
which it is addressed. It may contain information that is proprietary,
privileged or confidential or otherwise legally exempt from disclosure.
If you are not the named addressee, you are not authorized to read,
print, retain, copy or disseminate this message or any part of it.
If you have received this message in error, please notify the sender
immediately by e-mail and delete all copies of the message.

Radim Kolar

unread,
Jun 5, 2013, 8:21:09 PM6/5/13
to scala-l...@googlegroups.com

> It is if you assume that the thing to implement should not be limited to just the special case of tuples, but applies to pattern matching in general.
so instead of doing it just for tuples and be happy with it you decided
to implement it in broader scope which could not be done because it
conflicts with other features?

Simon Ochsenreither

unread,
Jun 5, 2013, 8:27:32 PM6/5/13
to scala-l...@googlegroups.com, h...@filez.com
Why should it be restricted to tuples in the first place?

Haoyi Li

unread,
Jun 5, 2013, 8:28:30 PM6/5/13
to scala-l...@googlegroups.com
so instead of doing it just for tuples and be happy with it you decided to implement it in broader scope which could not be done because it conflicts with other features?

You make it sound silly, but I think it is the right thing to do. "A feature that works 90% of the time, but breaks in the general case" sounds great, but when you end up with a whole bunch of features like that... things get messy. So far Scala's been pretty good at resisting the urge to take these shortcuts and holding off until it can be done "properly", and I think it's been better for it.


On Wed, Jun 5, 2013 at 8:21 PM, Radim Kolar <h...@filez.com> wrote:

It is if you assume that the thing to implement should not be limited to just the special case of tuples, but applies to pattern matching in general.
so instead of doing it just for tuples and be happy with it you decided to implement it in broader scope which could not be done because it conflicts with other features?

Radim Kolar

unread,
Jun 6, 2013, 8:21:28 AM6/6/13
to scala-l...@googlegroups.com
Dne 6.6.2013 2:27, Simon Ochsenreither napsal(a):
> Why should it be restricted to tuples in the first place?
1. easy to implement
2. no conflict with any existing feature
3. good enough solution for me

Lars Hupel

unread,
Jun 7, 2013, 2:21:27 AM6/7/13
to scala-l...@googlegroups.com
> 3. good enough solution for me

The proper solution would be not to use re-assignment anyway. There's no
need to add such a special-case (no pun intended) feature to the language.

Oliver Ruebenacker

unread,
Jun 7, 2013, 6:17:34 AM6/7/13
to scala-l...@googlegroups.com

     Hello,


  That's exactly what I am afraid of: FP-puritanism blocking language improvement.

     Take care
     Oliver

--
Head of Systems Biology Task Force at PanGenX (http://www.pangenx.com)
Any sufficiently advanced technology is indistinguishable from magic.

Radim Kolar

unread,
Jun 7, 2013, 3:38:44 PM6/7/13
to scala-l...@googlegroups.com
Dne 7.6.2013 12:17, Oliver Ruebenacker napsal(a):
> That's exactly what I am afraid of: FP-puritanism blocking language
improvement.
yes, i see it same way.

Simon Ochsenreither

unread,
Jun 7, 2013, 4:39:01 PM6/7/13
to scala-l...@googlegroups.com, h...@filez.com
I don't see it this way. I don't want Scala to end like C# or C++.
If you compare this feature request with the various features we got rid of recently because they weren't worth their weight, I'd say that this thing has a benefit/cost ratio which is even worse.

Oliver Ruebenacker

unread,
Jun 7, 2013, 5:56:40 PM6/7/13
to scala-l...@googlegroups.com, h...@filez.com

     Hello,

On Fri, Jun 7, 2013 at 4:39 PM, Simon Ochsenreither <simon.och...@gmail.com> wrote:
I don't see it this way. I don't want Scala to end like C# or C++.
If you compare this feature request with the various features we got rid of recently because they weren't worth their weight, I'd say that this thing has a benefit/cost ratio which is even worse.

  What cost?

  It seems allowing it for assignment what is allowed for initialization makes the language more consistent, and it is the distinction that adds extra cost.

     Take care
     Oliver

--
You received this message because you are subscribed to the Google Groups "scala-language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-languag...@googlegroups.com.

For more options, visit https://groups.google.com/groups/opt_out.
 
 

Simon Ochsenreither

unread,
Jun 7, 2013, 6:11:07 PM6/7/13
to scala-l...@googlegroups.com, h...@filez.com

  What cost?

  It seems allowing it for assignment what is allowed for initialization makes the language more consistent, and it is the distinction that adds extra cost.

The cost of adding a special feature which works only on tuples, which is inconsistent with the idea of pattern matching in general (“everything which defines an unapply method can be used in pattern-matching”).
Adding magic which says “oh, and there are some special rules which only work for the combination of variables, assignment and tuples” is not good enough.

Oliver Ruebenacker

unread,
Jun 7, 2013, 6:34:08 PM6/7/13
to scala-l...@googlegroups.com, h...@filez.com

     Hello,


  To me, making a distinction between the syntax for assignment and initialization is a much more glaring inconsistency (aka special rule, added magic, etc).

  But anyway - why the rules for initialization and assignment are different?

     Take care
     OIiver

Lars Hupel

unread,
Jun 9, 2013, 10:04:40 AM6/9/13
to scala-l...@googlegroups.com
> That's exactly what I am afraid of: FP-puritanism blocking language
> improvement.

That'd only be true if it were an improvement. We probably don't need
more special-casing for tuples in the language.

Oliver Ruebenacker

unread,
Jun 9, 2013, 10:19:31 AM6/9/13
to scala-l...@googlegroups.com

     Hello,

  Regardless of the merits of the proposal, to say that "we don't need to improve re-assignment as it should not be used" is FP-puritanism at its worst. People use, and should use, re-assignments in some cases.

  That aside, could some one explain why it would be more of a special case for assignment than for initialization?

  Thanks!

     Take care
     Oliver

--
You received this message because you are subscribed to the Google Groups "scala-language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-languag...@googlegroups.com.

For more options, visit https://groups.google.com/groups/opt_out.


Rex Kerr

unread,
Jun 9, 2013, 3:32:37 PM6/9/13
to scala-l...@googlegroups.com
Because the pattern matching _only works on initialization_.  It never works on assignment.
  val (a,b) = ("some", "fish")
  var (a,b) = ("some", "fish")
both work, and are unambiguous precisely because of the val/var keyword.

  --Rex

Ruslan Shevchenko

unread,
Jun 9, 2013, 4:57:46 PM6/9/13
to scala-l...@googlegroups.com

Btw, extending asigment to support pattern matching by doing initialization of temporary vals and doing actual assignment from those vials will work. And statements like (x,y)=(y,x) will have natural semantics.

Rex Kerr

unread,
Jun 9, 2013, 7:21:40 PM6/9/13
to scala-l...@googlegroups.com
I thought we already covered this.  Single-arg extractors are not distinguishable from single-element update calls (at the use site).

  Fox(x) = f(y)

means only one thing, now: Fox.update(x, f(y)).  If it could also mean var x = { val Fox(temp) = f(y); temp } we would be in for all kinds of confusion.

The difference this makes in how to think about the code and to generate correct code is too important to be left implicit.

If we ever get multiple return values on functions, then I think it's worth examining a multiple-value assignment.  Otherwise it just sticks out too much.

  --Rex

Oliver Ruebenacker

unread,
Jun 9, 2013, 7:53:50 PM6/9/13
to scala-l...@googlegroups.com

     Hello,

  And why does pattern matching work only on assignment?

  Thanks!

     Take care
     Oliver

Oliver Ruebenacker

unread,
Jun 9, 2013, 7:55:34 PM6/9/13
to scala-l...@googlegroups.com

     Hello,

  AFAICT, Tuple2 does not have a method named update, so I don't see how assignment to Tuple2 can be confused with an update call.

     Take care
     Oliver

Ruslan Shevchenko

unread,
Jun 10, 2013, 1:16:48 AM6/10/13
to scala-l...@googlegroups.com
Looking at grammar:  we have accurate hole for tuple assigments here.

i.e.  (page 161 of specification of 2.9.2  -- I guess 2.10 not changed here).

[SimpleExpr ‘.’] id ‘=’ Expr
SimpleExpr1 ArgumentExprs ‘=’ Expr 


1-st is translated to '_=' call,  second to 'update' call.

Note, that call of assignment to pairs does not satisfy to current syntax rules.
It is possible to change ones or  as adding optionality to ArgumentExpres in second rule
(in such case it will be possible to implement macros for pair update and this become a library issue)
 or insert special handling of tuple assigments.


Som Snytt

unread,
Aug 3, 2013, 9:19:25 PM8/3/13
to scala-l...@googlegroups.com

Why did the junior programmer visit the C guru?

She just wanted a few pointers!


I just changed the package name for this intupolator macro and also changed the extractor name to "*" (from "u").  I think the star is much funnier than a letter.

The macro supports tuple values and will use productIterator, see the example.  If the RHS looks like TupleN.apply, it will use the applied arguments directly, which means the Tuple can get optimized away (as well as boxing).

This was a learning exercise.  For instance, you can't say q"$tuple._$i" to interpolate the i first.  OK.

q remains the queerest ascii letter; if the Scala store had a quasiquotes t-shirt, I would get one.  It wouldn't even have to have a quote on it, because, like, quasiquotes is the quote.

Usage:

import pointers._
 
object Test extends App {

  var a, b, c = 0
  def pairing: Product = (11, 12)
   
  Console println s"a $a b $b"
  *(a, b) = (7, 8)
  //*(a, b, c) = (7, 8)   //intupolated.scala:13: error: Bad product arity (2) for assignment
  Console println s"a $a b $b"
  *(a, b) = pairing
  Console println s"a $a b $b"
  //*(a, b, c) = pairing  //java.lang.AssertionError: assertion failed: Bad product arity (2) for assignment
  //*(a, b) = ("a", "b")  //intupolated.scala:18: error: type mismatch;
}   
 

Implementation:

package pointers

import scala.language.experimental.macros
import scala.reflect.macros.Context

/** u(x,y,z) = p is u.update(x,y,z,p) => x = p._1 etc*/
object * {
  def update(args: Any*): Unit = macro intupolator

  // or perhaps entupolator
  def intupolator(c: Context)(args: c.Expr[Any]*): c.Expr[Unit] = {
    import c.literalUnit
    import c.universe._
    import scala.collection.mutable.ListBuffer
    val evals = ListBuffer[c.Tree]()
    val rhs = args.last.tree
    if (rhs.tpe <:< typeOf[Product]) {
      val ptname = "(?:Product|Tuple)(\\d+)".r
      def arityOf(n: Name) = n.toString match {
        case ptname(n) => n.toInt
        case "Product" => 0
        case _         => -1
      }
      val arity = arityOf(rhs.tpe.typeSymbol.name)
      if (arity > 0 && arity < args.length - 1) {
        c.abort(c.enclosingPosition, s"Bad product arity ($arity) for assignment")
      } else if (arity > 0) {
        def isTuple(n: Name) = n.toString startsWith "Tuple"
        def directly(vs: List[Tree]) = {
          args.init zip vs foreach { case (arg, v) => evals += q"$arg = $v" }
        }
        def indirectly() = {
          val x = TermName(c.freshName("x$"))
          evals += q"val $x = ${args.last}"
          var i = 1
          for (arg <- args.init) {
            val xi = Select(q"$x", TermName(s"_$i"))
            evals += q"$arg = $xi"
            i += 1
          }
        }
        rhs match {
          case Apply(TypeApply(Select(Select(Ident(TermName("scala")), n), TermName("apply")), _), vs)
            if isTuple(n) => directly(vs)
          case _          => indirectly()
        }
      } else {
        val x = TermName(c.freshName("x$"))
        evals += q"val $x = ${args.last}"
        val it = TermName(c.freshName("x$"))
        evals += q"""assert ($x.productArity >= ${args.length - 1}, "Bad product arity ("+ $x.productArity +") for assignment")"""
        evals += q"val $it = $x.productIterator"
        for (arg <- args.init) evals += q"$arg = $it.next.asInstanceOf[${arg.tree.tpe}]"
      }
    } else {
      c.abort(c.enclosingPosition, s"${args.last.tree.tpe} is not a Product")
    }
    c.Expr[Unit](Block(evals.toList, literalUnit.tree))
  }
}

Reply all
Reply to author
Forward
0 new messages