Discuss: Get/Set class property syntax (as kotlin's)

Scala allows to generate property like setters and getters backed by a private field by appending _= to method name

class Person {
  private var _name: String = ""
  
  def name: String = _name
  
  def name_=(newName: String): Unit = {
    if (newName.nonEmpty) {
      _name = newName
    }
  }
}

  // Usage
  val person = new Person()
  // gets name
  val name = person.name
  // sets name
  person.name = "John"

However kotlin has a much nicer syntax which makes scala looks kind of obscure

class Person {
    var name: String = ""
        get() = field
        set(value) {
            if (value.isEmpty()) {
                field = value
            }
        }
}

Full syntax definition for kotlin properties

var <propertyName>[: <PropertyType>] [= <property_initializer>]
    [<getter>]
    [<setter>]

Is that nice syntax good enough to be considered for adoption or improving the existing one?

Personally, I think getters and setters are something of an antipattern. If they are just getting and setting the underlying field–well, Scala var is that already, so if you must switch to a getter or setter that does something, you can in a binary compatible way.

And if they are doing something else–well, what are they doing?

If they change the input value, then you have x.thing = y; y == x.thing // false?!. That’s an invariant that is kind of annoying.

Input validation? But then you’re forced to handle validation with an exception, which tends to make your code fragile. Behind any innocent-looking assignment or retrieval, an exception might be lurking.

Side effects? Would kind of like to know that they’re happening most of the time.

Reactive processing? Don’t do it on bare fields! Use a library.

And so on.

There are, of course, still cases where they are genuinely helpful. But personally I am fine with them being as hard as they are now in order to discourage people from adopting bad habits simply because they’re syntactically a little easier.

9 Likes

Not sure if backend and frontend world will ever converge on scala, always seems like scala people only knows about REST APIs, but for anybody with enough experiende on UI will notice reactive properties are very common, from the old JavaFX beans to the increasingly popular Javascript Signals which may become a web standard, they are all based on properties, including a plethora of options like reactive UI Mobx on js/react, state delegates on kotlin/compose etc.

Reactive processing? Don’t do it on bare fields! Use a library.

What mechanism do you think most UI libraries currently use (besides fields/properties), im curious, seriously, btw reactjs is not reactive (in case you think of it)

Responding to your question, im currently using properties for scala based signals, which behave the same as in any other language, before saying a signal is an antipattern would recommend searching for it first

Disclaimer: Given the working background im biased towards properties, they are the main mechanism behind the reactivity of virtually every UI library/framework that does not suck, example: svelte runes, solidjs signals, vuejs reactivity, lit web… and so on

All of those seem to provide some abstraction such as this instead of requiring language-level properties.

class Person {
  val name = Var("") // or Signal("") or ref("")
}

val person = new Person
p.name() = "Martin"
println(p.name())
3 Likes

To be honest, I think the Kotlin syntax looks awful. I had to look trice what this is, and was still thinking you forgot some curly braces or formatted the code wrongly until I’ve seen the excerpt from the grammar. (Does this actually mean Kotlin has whitespace sensitive syntax?)

This property syntax looks as bad as the one of C# and JS. In both cases it’s a bolted on irregularity. I really like JS, but the property syntax was always one of the biggest WTFs there, imho. C# is not better (even the { get() } / { set() }thingy doesn’t look as out of place as in JS or Kotlin).

The Scala property syntax is not pretty either, but at least it’s not some outright irregularity (just some method name magic, which is a common pattern in Scala).

And looking at it from a more conceptual level, setters / getters are indeed a classical OOP anti-pattern. Setters / getters break encapsulation! State changes are something that should be internal to an object, and nothing triggered directly from the outside. All state changes should be only triggered through proper methods and not a glorified assignment from the outside.

I see the argument about reactive properties. (Native signals support would be also on my wishlist for Scala, but that’s another topic). But I think something like that is only relevant for library authors. For the end-user a (macro) annotation on some field (or some global code transformation, maybe by some compiler built-in support for “events” / “signals”) should give you reactivity. You should not need to program that by hand, ever.

So if anything I would like to have native signals. But I would not touch the getter / setter syntax.

6 Likes

All of those seem to provide some abstraction such as this instead of requiring language-level properties.

Not sure if we are getting close to mutual understanding, but your Var abstraction would use getters and setters internally, same as Signals and virtually every other reactive library, they are used by the authors but hidden to the consumers

The point is that at some level someone uses them, you can ignore that fact or accept it, if so, may also accept that they are also candidates for revision and evolution (even different from kotlin’s) or at least for some level discussion about them

The actual syntax works, the point i’m trying to bring into the table, is whether or not, the current syntax is something that should be revised or remain as it is

Given the comments above, even though there is consensus that scala properties have an “ugly syntax”, seems there is not enough interest to talk about revisions for it, tagged as anti-pattern, and that the library authors UX experience is a minor concern

Setter logic is part of the class definition, thus is internal behavior of the object, the only difference is that it’s triggered on assignment instead of a method call syntax, which are not entirely different as setter are just regular methods with support for assignment sugar syntax.

In that sense the setter scala syntax is closer to that notion of sugar syntax for regular methods, it only happens that the special syntax for assignment operator overload may look a bit obscure

I’m not saying library author concerns aren’t important.

But the only valid use-case for more involved setters I know of is building some reactive objects.

For everything else it’s imho an anti-pattern. Think code like:

world.state = "peace"

One would not expect this to trigger any “hidden actions”.

But what if the Wold class had a setter like

def state_=(newState: String): Unit =
   if newState == "peace" then
      launchMissiles()
   else
      _state = newState

An assignment should in almost all cases not have (side) effects!

That’s one of the things people were constantly criticizing about C++, namely the possibility to overload the assignment operator, which leads to “magic assignments”; which are a big WTF usually.

I see the use-case for reactive objects and “events”, though. But that’s imho completely independent of the idea to change the property syntax.

Maybe if there were some convincing concept to add “events” like in JS or C# (which are really a very nice feature, I miss in Scala) one could think to co-design also properties in a different way so that both features fit each other well.

But as we don’t have “events” I really don’t see the need to touch property syntax. It’s something rarely used, and it’s not inconvenient, just a little bit “ugly”. But adding extra syntax for a niche use-case seems wrong, imho.

I don’t find Kotlin’s syntax an improvement, but a rather surprising and alien use of assignment: it looks to me very strange that a method invocation can be assigned.

The Scala property story is entirely explainable to a beginner in a way that only requires 1) an understanding of what a method is and 2) what a field is and 3) the rule that enables the sugar (special name + getter). I also like that we have an explainable and consistent story around uniform access.

5 Likes