Questioning the necessity of `:` in object/class/enum/trait declarations

The current meta-rule is roughly “if it ends with a keyword you don’t put a :". Not sure how teachable that rule is, but that’s what Scala follows.

The extension case is a bit off an odd one out, for this reason and others, and it would be nice to normalize it

3 Likes

It hides the problem under the rug. This has revealed that learners of the language have a vague understanding of what meaning : carries. While beginners don’t have to confront their failure to understand the distinction if they can use the same spacing style for both type ascriptions and block-openers, the lack of understanding is still there.

Worse, if they write both the same way, some students might think that the : does mean the same thing in both scenarios, just in a subtle way they don’t yet understand. “maybe the : in class Foo: is a sort of meta type ascription? That I’m about to declare the type of the type Foo? And maybe xs.map: x => was picked as the notation for lambdas because it emphasizes some symmetry or analogy from type theory I’ve yet to grasp?”

I know, it’s just for teaching purposes. Anyway, I’m not trying to advocate for the style choice in this post, my point was just that it highlights that : should feel more coherent in the language.

I am actually in favor of a where delimiter, and especially of a standalone where as I find myself sometimes writing blocks with a leading def result:

def method: X = {
  def res: X = {
    sub1(sub2(...))
  }

  def sub1(...) = {...}

  def sub2(...) = {...}
  
  res
}

Where the def res is a workaround necessary only because, while you can call a local function before its definition, as in { sub1(...); def sub1={...} }, that call will not be the result of the block - the def after the call would override the result of the block to Unit. A where block would fix that: sub1(sub2) where { def sub1; def sub2 }

Regardless, I would be very surprised if where wasn’t tried as a delimiter either, there must have been a reason why with was chosen over where in given X with { ... } syntax. In fact with was itself used instead of : with syntax class X(...) with at one point and that was dropped in favor of :. In case anyone’s interested in why, they can find the historic discussion issues and multiple PRs with various syntax tryouts (especially for given declarations) in scala/scala3 repo and track when each syntax variant was floated and dropped and why.

On block syntax, my idea was to permit trailing defs:

def m =
  return f(42)
  def f(i: Int) = i + 27

where the result type is inferred.

I had a separate idea for leading defs, where a comment is currently required to locate where execution begins:

def m =
  def f = ???
  def g = ???
  begin m
  val i = 27
  return f + g + h(i)
  def h(i: Int) = ???

where the label ensures that previous definitions are not strict.

1 Like

That would actually add a very good use case for return! I just completely forgot it existed.