See: Can I specify a meaningful name for an anonymous class in C#?
In C# you can write:
var e = new { ID = 5, Name= "Prashant" }; assertEquals( 5, e.ID )
But in Scala I end up writing:
var e = (5, "Prashant") assertEquals( 5, e._1 )
Scala maintains type safety through the use of generics (as does C#), but loses the readability of the name of each field, e.g I use "_1" instead of "ID".
Is there anything like this in Scala?
In Scala, a tuple is a value that contains a fixed number of elements, each with its own type. Tuples are immutable. Tuples are especially handy for returning multiple values from a method.
Thankfully, Scala already has a built-in tuple type, which is an immutable data structure that we can use for holding up to 22 elements with different types.
object T { def main(args: Array[String]) { val e = new { var id = 5; var name = "Prashant" } assert(e.id == 5) } }
Ok, let's make stuff clear. This does use reflection on Scala 2.7 and Scala 2.8, because the type of e
is, in this case, a structural type, which Scala handles through reflection. Here is the generated code, at clean-up time (scalac -Xprint:cleanup
):
package <empty> { final class T extends java.lang.Object with ScalaObject { private <synthetic> <static> var reflMethod$Cache1: java.lang.reflect.Method = null; private <synthetic> <static> var reflClass$Cache1: java.lang.Class = null; <synthetic> <static> def reflMethod$Method1(x$1: java.lang.Class): java.lang.reflect.Method = { if (T.this.reflMethod$Cache1.eq(null).||(T.this.reflClass$Cache1.ne(x$1))) { T.this.reflMethod$Cache1 = x$1.getMethod("id", Array[java.lang.Class]{}); T.this.reflClass$Cache1 = x$1; () }; T.this.reflMethod$Cache1 }; @remote def $tag(): Int = scala.ScalaObject$class.$tag(T.this); def main(args: Array[java.lang.String]): Unit = { val e: java.lang.Object = { new T$$anon$1() }; scala.this.Predef.assert(scala.Int.unbox({ var exceptionResult1: java.lang.Object = _; try { exceptionResult1 = T.reflMethod$Method1(e.getClass()).invoke(e, Array[java.lang.Object]{}) } catch { case ($1$ @ (_: java.lang.reflect.InvocationTargetException)) => { exceptionResult1 = throw $1$.getCause() } }; exceptionResult1 }.$asInstanceOf[java.lang.Integer]()).==(5)) }; def this(): object T = { T.super.this(); () } }; final class T$$anon$1 extends java.lang.Object { private[this] var id: Int = _; <accessor> def id(): Int = T$$anon$1.this.id; <accessor> def id_=(x$1: Int): Unit = T$$anon$1.this.id = x$1; private[this] var name: java.lang.String = _; <accessor> def name(): java.lang.String = T$$anon$1.this.name; <accessor> def name_=(x$1: java.lang.String): Unit = T$$anon$1.this.name = x$1; def this(): T$$anon$1 = { T$$anon$1.this.id = 5; T$$anon$1.this.name = "Prashant"; T$$anon$1.super.this(); () } } }
There is some caching going on, but if I alternated between id
and name
it would invalidate the cache already. Scala 2.8 also does reflection, and also caches, but it uses a more efficient caching technique, which should provide better overall performance. For reference, here is the clean-up of Scala 2.8:
package <empty> { final class T extends java.lang.Object with ScalaObject { final private <synthetic> <static> var reflParams$Cache1: Array[java.lang.Class] = Array[java.lang.Class]{}; @volatile private <synthetic> <static> var reflPoly$Cache1: scala.runtime.MethodCache = new scala.runtime.EmptyMethodCache(); <synthetic> <static> def reflMethod$Method1(x$1: java.lang.Class): java.lang.reflect.Method = { var method1: java.lang.reflect.Method = T.reflPoly$Cache1.find(x$1); if (method1.ne(null)) return method1 else { method1 = x$1.getMethod("id", T.reflParams$Cache1); T.reflPoly$Cache1 = T.reflPoly$Cache1.add(x$1, method1); return method1 } }; def main(args: Array[java.lang.String]): Unit = { val e: java.lang.Object = { new T$$anon$1() }; scala.this.Predef.assert(scala.Int.unbox({ val qual1: java.lang.Object = e; { var exceptionResult1: java.lang.Object = _; try { exceptionResult1 = T.reflMethod$Method1(qual1.getClass()).invoke(qual1, Array[java.lang.Object]{}) } catch { case ($1$ @ (_: java.lang.reflect.InvocationTargetException)) => { exceptionResult1 = throw $1$.getCause() } }; exceptionResult1 }.$asInstanceOf[java.lang.Integer]() }).==(5)) }; def this(): object T = { T.reflParams$Cache1 = Array[java.lang.Class]{}; T.reflPoly$Cache1 = new scala.runtime.EmptyMethodCache(); T.super.this(); () } }; final class T$$anon$1 extends java.lang.Object { private[this] var id: Int = _; <accessor> def id(): Int = T$$anon$1.this.id; <accessor> def id_=(x$1: Int): Unit = T$$anon$1.this.id = x$1; private[this] var name: java.lang.String = _; <accessor> def name(): java.lang.String = T$$anon$1.this.name; <accessor> def name_=(x$1: java.lang.String): Unit = T$$anon$1.this.name = x$1; def this(): T$$anon$1 = { T$$anon$1.super.this(); T$$anon$1.this.id = 5; T$$anon$1.this.name = "Prashant"; () } } }
You can also name the parts of the tuple you're assigning to, as in:
val (ID, Name) = (5, "Prashant") assertEquals( 5, ID )
You can also use this like:
val (ID, Name, Age) = functionThatReturnsATuple3 println("ID: " + ID + ", age: " + Age)
When I first read about the _x
syntax I thought it was great and used it a lot. I've since basically stopped using it as when I have to look at code I wrote two months ago I have to spend a load of time trying to work out what the types of the _1
, _2
etc. are. I suppose it's obvious in hindsight that id
is much more readable than pair._1
.
This can also be used inside functions like map
, filter
etc. like:
val list: List[ (Int, String, Double) ] = ... list map { case (id, name, time) => ... } list filter { case (_, name, _) => name == "X" }
Note that in the filter
you can use _
s for elements which you aren't going to use in the body of the function. This can be useful when skimming over code like that to establish what parts of structures are being used and how values are built up.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With