Deprecate assignment to underscore

To relax the overloaded meaning of _ just a bit, it would make sense to at some point in the future disallow assignments such as val x: String = _ and var x: String = _.

In some cases the assignment promotes mistakes, like with var perhaps: Option[String] = _, while non-reference types should be initialized with a meaningful value. var counter: Int = _ is just as long as var counter: Int = 0 but the latter just makes more sense.

Isn’t only allowed for var?
Also IIUC valhalla value classes could have their own default value?

.NET has a good answer for this, that Scala use for inspiration. default

Scala could have

case class Default[A](value: A)

object Predef {
  def default[A](implicit instance: Default[A]): A = instance.value
}

object Int {
  val default: Default[Int] = Default(0)
}

object AnyRef {
  val default: Default[AnyRef] = Default(null)
}

And _ would just be the same as default. Scalac would be able to remove calls to default when the JVM would already initialize a value to the correct value, e.g. for references or values. For more complicated types, you could have

object Option {
  private val defaultInstance: Default[Option[Nothing]] = Default(None)

  implicit def default[A]: Default[Option[A]] = defaultInstance
}

And @megri that would give you the more meaningful values for _ that you’re looking for.

Sure, you can implement that today. Something like

trait Default[A] {
  def value: A
}

// Predef.scala

@inline implicit def default[A](implicit d: Default[A]): A = d.value // transparent/opaque or whatever it's called in Scala 3

implicit object IntDefault extends Default[Int] {
  val value: Int = 0
}

implicit def AnyRefDefault[A <: AnyRef] = new Default[A] {
  val value: A = null.asInstanceOf[A]
}

// Elsewhere.scala

var x = default[Int] // 0: Int
var y = default[String] // null: String

Point being, it doesn’t make much sense to build up the type hierarchy only to let _ be hardwired to default[T] in this specific scenario (and have a several meanings in other places).

1 Like

Ah, you’re right about val. I could’ve sworn it worked last time I tested it… :blush:

Who knows how value classes will work when they finally arrive. In any case it in itself seems like a bad reason to keep assignment to _ around, IMHO.

@megri Why shouldn’t _ be hardwired to default? It seems just as reasonable to me as any of the other hard wirings scalac does.

The examples don’t account for the change in semantics, as underscore really means the default and is not an assignment. That’s why I would prefer “self-assignment” as an idiom, if I were to use it, because it explicitly means “whatever value it has”. Maybe default initializers could be specified to be ordered before other constructor statements.

scala 2.13.0-M4> class C { var c: Int = _ }
defined class C

scala 2.13.0-M4> new C().c
res0: Int = 0

scala 2.13.0-M4> class C { c = 42 ; var c: Int = _ }
                           ^
                 warning: Reference to uninitialized variable c
defined class C

scala 2.13.0-M4> new C().c
res1: Int = 42

scala 2.13.0-M4> class C { c = 42 ; var c: Int = 0 }
                           ^
                 warning: Reference to uninitialized variable c
defined class C

scala 2.13.0-M4> new C().c
res2: Int = 0

scala 2.13.0-M4> class C { c = 42 ; var c: Int = c }
                                                 ^
                 warning: variable c in class C does nothing other than call itself recursively
                           ^
                 warning: Reference to uninitialized variable c
defined class C

scala 2.13.0-M4> new C().c
res3: Int = 42
1 Like

I found that scalaz has a Default, although it’s only for testing.

@som-snytt That’s interesting about the semantics of _ and the ordering of assignment and declaration in the constructor.

For now anyway, if someone wants fancier notion of default, it’s certainly possible now, and it’s not difficult. It’s perfectly good to not have it baked into the language.

Feel free to try it! I published this to maven central so it will appear soon.

1 Like

I don’t think default values for types are sound. Surely what we need is to be able to annotate operations (methods) as monoids, and give the operation an identity value.

I just don’t think this relatively uncommon case deserves special syntax. I’ve seen people often argue that _ has too many meanings today; this would reduce that count by 1.

2 Likes

Yeah, agreed. This isn’t used much that I’ve seen; it’s another overload of _; it causes more than its fair share of confusion among newbies; and it can easily introduce bugs if you don’t know what you’re doing. I think it’s one of those places where the language got a little too “clever”, and would benefit from dropping it.

Using underscore as a default initializer is an anti-pattern. If the type is known to be AnyRef or some AnyVal subtype, we can just put the desired value directly; if not, it won’t work anyway.

Deprecate assignment to underscore

I guess it was invented to support things like this:

class VaribleBox[T] {
  var value:T = _
}

new VaribleBox[Int].value        //0
new VaribleBox[String].value   //null

maybe something like this would be enough:

//instead of this
val c:T = _
//we could chose one of those
val c:T = Any.default[T] // don't like this one. Name default could be misleading  
val c:T = Any.emptyOf[T]
val c:T = Any.emptyValue[T]
val c:T = Null.valueFor[T]

I guess this could be introduced even in 2.13 and old notation could be simply deprecated. This is rather not popular feature.

Default/Empty typeclass

proposal to add Default or Empty typeclass or method somwhere in standard library seams to satisfy all users needs, but it needs to be carefully implemented.

assume that there should be implicit val default ....

This is to limited i guess! This pollutes user-space to much and does not give any flexibility! Default/Empty typeclass if introduced should give something more than simple null!

It could be implemented as something that provides instances for simple types

trait Default[T] {def get():T }
object Default {
  def apply[T](a:T) = new Default[T] { def get():T = a }
  implicit val intDefault = apply(0)
  implicit val doubleDefault = apply(0) 
  implicit val stringDefault = apply("")
  ...
}

and’ll generate instances for anything that have default constructor or apply() method on companion object (this cannot be representet typesafely in current scala as i know).
For types that has nothing like that it’ll just not compile. Not sure now where it could be needed (and this is sign that probably we should ignore this idea).

To be fair, this only works in the Int case because null.asInstanceOf[Int] == 0, as T is not specialized and thus simply Object in bytecode.

It’s the other way around. null.asInstanceOf[Int] == 0 only works because that’s needed to correctly support generic fields with = _.

2 Likes

Okay, now I have to chime in. I consider it critical that we be able to get a default value for a type parameter in a way that is accessible for fairly novice coders. I don’t care exactly what it is. I’m fine with var default: A = _ or null.instanceOf[A], or something else that takes about that much code. The reason is that there are data structures that are taught in early semesters of CS that really benefit from having a default value. The most obvious example is the sentinel node in a doubly-linked list. Any proposal that makes this more difficult would have a negative impact on the teachability of Scala in early CS courses.

1 Like

As long as you never pass it back to the user of the collection (so ok for a sentinel), null.asInstanceOf[A] will always work, because of erasure.

1 Like

As long as that doesn’t change, I’m good. I don’t really care exactly how I get a default value for a type parameter as long as there is always a simple way to do it that I can explain fairly easily to students.

I really don’t see the need for the language to change. If you want different behavior than _, what are you wanting that can’t be had from a library? I really recommend that anyone who is serious about getting an alternative to _ first use a library. I published mine to Maven Central and you’re free to fork it and play with it.

@scalway Yes you’re right I forgot the implicit keyword in my post. The later code you gave is basically the same as what I wrote for https://github.com/shawjef3/scala-defaults/blob/master/src/main/scala/me/jeffshaw/default/Default.scala#L12.