Statically obtain the name of a class member, in a checked way


#1

By the way, are there any plans to make member name access via this notation ?
For example

  • In Kotlin on can write
    assert (someObj::someMember.name == "someMember")
    (using KCallable::Name)
  • In JavaScript it will be simply
    console.assert (someObj.someMethodMember.name == "someMethodMember")
    (using Function.prototype.name)
  • While in C# nameof operator used instead
    Debug.Assert (nameof(someObj.someMember) == "someMember");

Last (C#-like) form looks more simple to implement, however approach used in Kotlin / JavaScript looks more intuitive.
So the question is, what should be the resulting type of eta expansion ? (just function? or could it be something enriched with some reflection-like properties?)


Proposal to add Automatic Eta Expansion to the Language
Proposal to add Automatic Eta Expansion to the Language
#2

I hope no. Why ever you think you may need this?


#3

Well, it is basically safe way to get name of method and be sure that it will not be corrupted by next refactorings (renames of that method). If you just hardcode “someMember” smart refactoring tools probably will suggest you to change that hardcode as well, but it will be definitely not safe way of keeping code consistent


#4

You should be able to achieve a similar effect using https://github.com/lihaoyi/sourcecode (if it doesn’t support your use case, which I’m not clear on, you could submit an improvement).


#5

It looks that mentioned library fits logging needs very good. However use case that I keep in mind stands little bit aside (in fact it is bit irrelevant because it more about properties, but still I found it very close to discussed topic).

So let’s consider models that should be stored in MongoDb

case class Bar(val bar: String)

case class Foo(val foo: Bar)

MongoDb query to imaginary Foo-collection may look like

{"foo.bar": "v43"}

And here, if you would like to write some simple code implementing that query, you would probably just hardcode string “foo.bar” (it may simply look like BSONDocument("foo.bar" -> "v43"), by the way recently I’ve spotted some examples of this kind in scastie/MongoDBSnippetsContainer.scala, L103, L115, L177 )

In Kotlin "foo.bar" could be encoded as "${Foo::foo.name}.${Bar::bar.name}" which make that MongoDb filter encoding more verbose, but at least little bit more safe. So I was thinking that having something similar in Scala also could be useful in some cases.

However problem there is also in that while Foo::foo in Kotlin is KProperty1 (which is also a function (T) -> R), in Scala Foo.foo is just invalid notation (since foo is member of class, and not a member of companion object …). So even using nameOf macros, (which one may expect should solve this problem) notation
s"${nameOf(Foo.foo)}.${nameOf(Bar.bar)}" still will be invalid, other notation
s"${nameOf((null:Foo).foo)}.${nameOf((null:Bar).bar)}" will work, but looks too dirty to be suggested as solution


#6

I invite you to read this article, https://adelbertc.github.io/posts/2019-01-07-protocols.html, which gives motivation to not base your serialized format on your Scala types.

In case you still want to do that, you can use “generic” serializers such as reactivemongo-derived-codecs.


#7

In C# I use this semi-frequently, in the pattern

public int foo(int number) {
  if (number < 0) throw new ArgumentException("should be larger than 0", nameof(number));
   //the rest
}

in scala, I could imagine the rough equivalent

def foo(i: Int): Either[String, Int] = {
   if (number < 0) Left(s"argument ${nameof(number)} should be larger than 0")
   else Right {
     //the rest
    }
} 

being useful


#8

You can do it now with the mentioned earlier (in the original topic) nameof plugin. See, for instance, your example working here. There is no need to change the language to support this.


#9

Your question was why anyone would want that. Well, everyone who uses the nameof plugin wants that for example.

Don’t get me wrong, I don’t think this is a feature that would be a good addition to the language, but there are good uses of this feature.


#10

I find being able to fetch names very useful. It allows automation of some aspects of logging, for example. I used the feature in a little toy reasoning language where each val contained one inference rule, and during tracing the reasoning chain, it would tag each inference with the name of the rule that was applied. When used together with back tick names, it lets you write self-literate programs. I would definitely like to see this as part of the core reflection library in dotty. Summon names as implicit arguments, ideally.


#11

I created a PR to the nameOf library:

In addition to nameOf(member) you can do member.scalaName

I don’t know if the author of the original library still maintains it (last commit was 2 years ago), but you can use my fork instead.