Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to have tuples with named fields in Scala, similar to anonymous classes in C#?

Tags:

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?

like image 477
Alex Black Avatar asked Dec 01 '09 17:12

Alex Black


People also ask

What is a benefit of using tuples 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.

Can we have variables of different types inside a tuple Scala?

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.


2 Answers

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";       ()     }   } } 
like image 112
Daniel C. Sobral Avatar answered Sep 28 '22 03:09

Daniel C. Sobral


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.

like image 32
ams Avatar answered Sep 28 '22 05:09

ams