question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

SIP: Widening of case class apply to better emulate ADTs

See original GitHub issue

Motivation

In Scala, case classes are used to emulate algebraic data types. But the emulation is not exact, insofar as a case class represents its own type whereas a constructor of an ADT has as result type the type of the underlying ADT type. For instance, using

trait Option[+T]
case class Some[T](x: T) extends Option[T]
case object None extends Option[Nothing]

the type of Some(1) is Some[Int], not Option[Int]. While useful, this can also cause problems for type inference. This SIP proposes an alternative class and object syntax, using : for extends. The only difference between the two forms concerns the auto-generated apply method of a case class. If extension is via a :, apply has as return type the conjunction of all parent types following the :.

Example: Using

trait Option[+T]
case class Some[T](x: T): Option[T]
case object None: Option[Nothing]

we have as most precise typings:

Some(1): Option[Int]
None: Option[Nothing]

Proposal in detail

In SyntaxSummary.txt change

TemplateOpt       ::=  [`extends' Template | [nl] TemplateBody]

to

TemplateOpt       ::=  [(`extends' | `:') Template | [nl] TemplateBody]

Add the restriction that : can be used only for case classes. (Not sure we need this, should we allow `:’ for normal classes as well? It would make no difference there).

Augment the definition in the spec on Case Classes as follows.

If the case class definition is defined with a : extension clause, as in C[tps](ps1)...(psn): parents {...} the following apply method is generated instead, where P1, ..., Pn are the declared or inferred parent types of C:

object C {
 def apply[tps](ps1)...(psn): P1 & ... & Pn = new C[tps](xs1)...(xsn)
}

Augment the definition in the spec on object definitions as follows: After the existing

It is roughly equivalent to the following definition of a lazy value:

    lazy val m = new P1 with ... Pn { this: m.type => stats }

add the clause: if the case object definition is defined with a : extension clause, the associated object is given the type P1 & ... & Pn, as in:

lazy val m: P1 & ... & Pn = new P1 with ... Pn { this: m.type => stats }    

Issue Analytics

  • State:closed
  • Created 8 years ago
  • Reactions:1
  • Comments:45 (26 by maintainers)

github_iconTop GitHub Comments

6reactions
nafgcommented, May 2, 2016

@odersky

@nafg It would be much simpler than proper enums.

Perhaps simpler to change the spec, but it would be a much greater cognitive stumbling block. There’s nothing intuitive about adopting C++/C#'s “extends” keyword (the :) but only in certain places, and the difference has to do with something random.

I would be more open to the following, instead of only allowing it on case classes:

If a : is used in a class definition in pace of extends, then the compiler will generate an apply method on the companion taking the same arguments as the primary constructor with a return type of the type following the :. If the class is a case class, then this apply method is generated instead of the normal case class apply method (i.e. with the class type as its return type).

Thus, : becomes a general rule, that intersects with case class simply by taking precedence in the one area of conflict.

However, ONCE AGAIN, WE REALLY TRULY SERIOUSLY VERY BADLY need a lightweight syntax for defining ADTs and enums. So can we PLEASE PLEASE PLEASE get syntax for that, and then revisit the “problem” that is the premise of this issue in that context?

6reactions
Scisscommented, Apr 30, 2016

I think this vastly complicates the language and the cognitive burden on people learning Scala. What’s wrong with allowing one to override the apply method of the companion object, like:

object Some {
  def apply[A](x: A): Option[A] = new Some(x)
}
case class Some[A](x: A) extends Option[A]

Another possibility would be to add an annotation, something like

@apply[Option] case class Option[A](x: A) extends Option[A]

But in general we already have Option.empty and Option.apply. You can use those if needed.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Pre-SIP: Structural data structures that can evolve in a binary ...
Case classes are often preferred to simple classes by Scala developers to model data structures. However, case classes have a drawback: they can ......
Read more >
In Scala why is it good practice to add case classes to ... - Quora
Case classes are great. I use them heavily and they are very easy to use. However they have a few issues. The worst...
Read more >
SIP and VoIP
Out-of-band signaling is more efficient; it ... Each node can use a SIP proxy for call setup ... In that case, the data...
Read more >
scala - May mix-in traits extend case classes from a technical ...
Extending case class with another class is unnatural because case class represents ADT so it models only data - not behavior.
Read more >
Extending Scala Case Class With NoStackTrace Leads To ...
Extending a Scala case class with NoStackTrace leads to unexpected toString behaviour.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found