tags:

views:

351

answers:

3

See: http://stackoverflow.com/questions/793415/use-of-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?

+1  A: 

Scala 2.8 has improved the type system to the point that it is possible to have statically and heterogeneously typed arrays and lists, so presumably the same could be done with maps. Check out Jesper Nordenberg's blog for his implementation (see the Saturday, September 12, 2009 entry, which is the newest as of this message).

Randall Schulz
Can you elaborate? Looking at Jesper's post it looks like its just another way to implement Tuples in scala, but without having to have Tuple1, Tuple2, Tuple3, etc. I'm not yet seeing how it could address the name of the fields within the tuple.
Alex Black
"... presumably the same could be done with maps." Left as an exercise for the reader, of course.Anyway, Daniel's solution is preferable if the fields are fixed at the time the instance is created.
Randall Schulz
By "done with maps", do you mean have a map where each item is of a different type, and the field names are used as keys?
Alex Black
That's what I was thinking, but I didn't think about it much.
Randall Schulz
+5  A: 
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";
      ()
    }
  }
}
Daniel
Clever. Does this create a new anonymous AnyRef subclass with those id and name members?
joev
Yes, it does. It won't perform well with Scala 2.7, IIRC, because it would use reflection for it. I'm not sure, but if it matters, you should check it out.
Daniel
I don't know why that would be so. The compiler has access to the (anonymous) class definition, so there should be no need for reflection. Only structural typing entails use of reflection, as far as I know.
Randall Schulz
Interesting.. when would reflection get used? Will 2.8 avoid the need for reflection?
Alex Black
Thanks for all the detail, looks interesting. It'd be great one day if Scala supported this without reflection like C# does.
Alex Black
But can you guarantee that C# doesn't do the same thing? :-)
Daniel
No, I can't, we'd have to ask someone from MS. But I am 99% sure no reflection is involved. For example, use of anonymous classes is type-safe and checked at compile time, and known in the IDE, so it seems unlikely reflection (a runtime feature) would be needed.
Alex Black
Looks to me like C# just generates a regular (but anonymous) class for you. http://www.codeguru.com/csharp/csharp/cs_misc/designtechniques/article.php/c11551__2/
Alex Black
In Scala, anonymous classes are type-safe and checked at compile time. The language designers here made a decision not to pollute things by creating a new thunk with a named class for each type of object that you could pass when using structural types as arguments. I don't know how long they'll keep doing this -- I'd imagine a major under-the-hood refactoring the Scala compiler when JDK 7 releases with several new VM features speed this kind of thing up, and when they see how Java 7 handles closures.
Ken Bloom
Just to clarify further -- the reflection is "under-the-hood" stuff for how they Scala implements this stuff. These types are still perfectly type safe and checked by the compiler. (Maybe the reflection helps with Java interoperability.)
Ken Bloom
A: 

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.

ams
That looks interesting - but can you use it for projection, e.g. mapping a list of items to named tuples?
Alex Black
I'm not quite sure what you mean, you can do: val list: List[ (Int, String, Double) ] = ...; list map { case (id, name, time) => ... }
ams
In C# you could do: employees.map( e => new { FirstName = e.Name.First, LastName = e.Name.Last } ).toList for example, which 'projects' the list of employees into a list of anonymous name classes. So in Scala you could do: employees.map( e => ((e.Name.First, e.Name.Last)) ).toList but you lose the names of the fields in the tuple.
Alex Black
In fact, in C# (you're probably sick of the C# comments I know) you can do employees.map( e => new { e.Name.First, e.Name.Last } ).toList which will use an anonymous class with fields with names First and Last automagically.
Alex Black