Type of an anonymous class derivation

Now, one is able to write the code:

val x = new {
  val r = 0
}

The code will be compiled without errors, but the type of x will be just “Object”. It seems strange, because we have all the information about the class, except its name. So, the type must be

AnyRef {val r: Int}

or, maybe, some type, whose name are generated. Intellij Idea, by the way, now says, that the type is

Object {val r: Int}

but Scala compiler doesn’t think so, and you cannot call x.r in the next line.

This way, the construction is rather useless, because nothing useful can be done with a plain Object.

On the other hand, if the real type was derived, we would do nice things, such as

  collection.view
    .map { x =>
      new {
        val value = x
        val result = veryLongComputation(x)
      }
    }
    .filter(_.result > 0.5)
    .foreach(x => println(s"result(${x.value}) = ${x.result}"))

Of course, in this simple case, one may use just a Tuple2, but its fields have no sensible names, and, all in all, you are able to do much more with a class than with a Tuple.

Inside “map” compiler knows all about the anonymous class. Its type should be

AnyRef {val value: Int; val result: Int}

So, the type of the result of “map” calling should be

CollectionViewType[ AnyRef { val value: Int; val result: Int } ]

So, the type of the argument of the “filter” should be

AnyRef { val value: Int; val result: Int }

etc.

And here is no boilerplate code, as if we would need to define the temporary class before the collection call-chain.

BTW, it can be done in Java:

var x = new Object() {
  public int r = 42;
};
System.out.println(x.r);
1 Like

You can just do:

import scala.reflect.Selectable
val x = new Selectable {
  val r = 0
}
println(x.r)
4 Likes

This won’t solve the larger problem, but in this specific case, named tuples would come in handy if they’re ever implemented.

1 Like

Huh, I didn’t realize this behavior had changed. Works fine in Scala 2. It surprises me that this didn’t break things. It doesn’t even work if you ascribe the refinement.

scala> val x = new { val r = 0 } : AnyRef { val r: Int }                                      
val x: AnyRef{r: Int} = anon$1@56554e7

scala> x.r
1 |x.r
  |^^^
  |value r is not a member of x
2 Likes

The doc on this is Type Inference | Scala 3 Migration Guide | Scala Documentation

1 Like

Try using some other name besides x. Or rm -rf x first, YOLO.

Number of days since somebody was bitten by Dotty bug 12571: 0

6 Likes

sideshow_bob.gif

Yep, in my home directory I have a scala folder and inside that a folder called x.

By the time I run into this again I will have forgotten about this bug (again) so we can all look forward to that.

5 Likes

I’ve read about this change, and because of it I think, that, now, it should be not a reflective call, but an anonymous class instancing with the type derivation. It will have better performance, in that case. And fields/methods declaration doubling will be not needed.

And I suggest an almost obvious use-case for such constructions, which make sense only if it can be local and is short.

By the way, the performance of the dynamics will be better, if the compiler inlines the reflective method calls by the static instances of the Method class, instead of getting a Method instance by its name every time.

“Selectable” trait is not the same. It is a dynamic class, and if you look inside its code, you’ll see, there is a reflective call without any caching, so its performance isn’t good. And, of course, there will be no hints from an IDE.

If I correctly understood this doc in case of local Selectable it will create refined type, if it’s well formed.

Now question, is it possible or welcomed for local instantiation to make syntax new {} to be alias for new reflect.Selectable {} for the sake of compact syntax?

For Selectable, the compiler translates a code like

element.z

to the

element.selectDynamic("z")

Inside the “selectDymanic”, the field “z” is found in the class by its name with reflection, and “get” method is called for the “element” instance.

It will have not the best performance, because of finding the reflective accessor on every call without any caching. All in all, this way is OK, when we need to similarly call methods with the same signature in different classes, but it is not good, when we want to make temporary instances. “Selectable” should exist as an option for some cases (Data Base records, for example), but not as the common way.

True, I’ve verified this as well, honestly after reading the doc I expected non-reflective access when Selectable is well defined

2 Likes