Currently when destructuring case class using named parameters, we have to specify both the parameter name, as well as what we’d like to call the extracted value:
case class User(firstName: String, lastName: String)
val User(firstName = firstName) = User("joe", "shmo") // firstName is now a val with value "joe"
Some other languages that allow similar destructuring offer shortcuts when the extracted variable name is the same as the parameter name, for example in javascript, you can destructure an object like
const user = { firstName: 'joe', lastName: 'shmo' }
const { firstName } = user // firstName is now a const with value 'joe'
or with dart, you can do
final User user = User(firstName: 'joe', lastName: 'shmo');
final User(:firstName) = user // firstName is now a final var with value 'joe'
My suggestion is to bring something like this to scala named-tuple/case class destructuring as well. Not sure what the exact syntax may look like, but perhaps something like dart? Colons are usually used for type ascriptions, but . has been suggested in similar implied field access in this discussion, so maybe we could have something that looks like
val User(.firstName) = user // firstName is now a val with value "joe"
If we keep it symmetric with the existing named argument syntax then it should be like this:
val User(firstName =) = user
or
val User(=firstName) = user
i guess the second makes more sense as its the RHS that is the pattern - so in this case =firstName means a wildcard placeholder for the firstName parameter
I do agree that =paramName is more consistent, but aesthetically I do prefer the dot syntax .paramName. The dot looks more like field access, which I think is closer to what is actually happening than passing a named argument.
EDIT: I must admit the dart example uses : though, which is how values are passed to named parameters
I also think the reverse operation, to create rather than destructure instances, could be helpful, where if a variable exists with the same name and type as a case class parameter, we could write something like val user = User(=name, =age) instead of val user = User(name = name, age = age)
I don’t like this idea much. It conflates the naming of the parameters at the definition site to the local variables at the usage site. This is a refactor tool nightmare, and bugs waiting to happen.
It’s adding complexity to the language, which is already complex enough. The proposed syntax might conflict with a more important feature we will come up in the future.
In my 20 year software career I never needed a feature that extracts a bunch of variables into local scope. I think it can mostly be avoided by structuring your data better. If you like verbosity that much, I am pretty sure you do not mind also typing the names twice.
What are we optimizing for? So code can be written faster? You should not optimize for code writing speed (especially now with AI). One should optimize for code reading speed, which this barely improves (maybe even worsens due to larger cognitive load).
case class User(firstName: String, lastName: String)
object Test:
val User(firstName = firstName) = User("joe", "shmo") // firstName is now a val with value "joe"
def show(): Unit = println(firstName)
Test.show()
And indeed, this does not compile (3.8.3):
val User(firstName = firstName) = User("joe", "shmo")
^
')' expected, but '=' found
case class User(firstName: String, lastName: String)
object Test:
val User(firstName,lastName) = User("joe", "shmo") // firstName is now a val with value "joe"
def show(): Unit = println(firstName)
Test.show()
it neatly shows “joe”. Really, i was unaware of this “feature”. Nice, never needed it, and i think we should not change it.
Sure – that’s been true more or less forever. AFAIK it’s exactly the same feature as why you can destructure the case values in match expressions, like:
match person {
case User(firstName, lastName) => ...
}
Aha, yes, that syntax is much used of course. But now i see the problem i missed. It is not that “=” is incorrect, it is just missing the second parameter.
Actually, a better way to enable what author wants is to just use import.
case class X(a: Int, b: String)
def f(x: X): Unit = {
import x.{a, b}
println(s"a = $a, b = $b")
}
f(X(10, "c")) // works!
However, I never needed this. In my whole large codebase you will rarely find a function with more than 3 parameters, or class with 5 parameters. You just need to structure your data better.
This does actually compile when using 3.8.3 in a project build via sbt locally, and have also confirmed it with scala-cli. There was a bug that was fixed in 3.8.3 itself that allowed it to work, not sure if scastie is behind somehow.
Ah, indeed you are correct, thanks for pointing this out. But then, what is left there to improve? If you do not want to use parameter names, you can use this:
val User(firstName,_) = User("joe", "shmo")
and otherwise use the named form, so you do not have to specify all parameters. This feels perfectly natural to me. Use either the named or positional syntax. And if you use the named one, then you should also be specifying the names, even when the parameter and variable names are equal.
Adding yet an other syntax for some minor character reduction gain would confuse more than it brings, imho.