0

I want to force case subclasses constructors to have a defined creation signature (by using a trait). However, I can't reference a class constructor inside the trait:

scala> :paste
// Entering paste mode (ctrl-D to finish)

trait MyTrait {

  def apply[A <: MyTrait](i: Int): A = {
    // Neither work:
    // new this(i)
    // new this.getClass.getMethod("apply")
    this(i)
  }

  def field: Int

  def increase(i: Int) = {
    this(this.field + i)
  }

}

case class MyClass01(field: Int) extends MyTrait {}


// Exiting paste mode, now interpreting.

trait MyTrait
class MyClass01

scala> val mc = MyClass01(0)

val mc: MyClass01 = MyClass01(0)

scala> val mc = MyClass01(0).increase(10)
                                     ^
       warning: dead code following this construct

(To diagnose errors in synthetic code, try adding `// show` to the end of your input.)
java.lang.StackOverflowError
  at MyTrait.apply(<pastie>:6)
  at MyTrait.apply$(<pastie>:3)
  at MyClass01.apply(<pastie>:17)
  ⋯

How do I do this in scala?


The idea is to force some case classes to have a given constructor:

case class MyClass02(field: Int) extends MyTrait {}
case class MyClass03(field: Int) extends MyTrait {}
⋯
fmv1992
  • 322
  • 1
  • 4
  • 14
  • I do not understand the question / problem? Is the question how to define a constructor in a trait such that all sub classes have to override that constructor? If so, what does increase and that error had to do with that. - Also, if that is the question, the answer is not possible, constructors are weird, they are not inherited, can not be overridden nor specified in an interface, they also have some weird limitations normal methods do not have. The best you could do, would be to create another trait for companion objects that defines a custom `apply` but that would be quite manual. – Luis Miguel Mejía Suárez Jan 02 '21 at 22:48
  • 1
    I would guess that you really do not want to define a constructor in an interface, but rather that was the solution you came with for your meta problem. - Care to describe what are you trying to do and why you think that would solve the problem. – Luis Miguel Mejía Suárez Jan 02 '21 at 22:50
  • Yes, I want to enforce that all case classes support a given constructor. (working on the second answer) – fmv1992 Jan 02 '21 at 22:50
  • It's also worth noting that `apply` is not a constructor. – Levi Ramsey Jan 02 '21 at 22:59
  • Full problem: I'm working on a parser with intermediate state (trait). It could have several sub classes such as `Comments`, `Whitespaces`, etc and I was able to come up with a good design except on that part. The code: https://github.com/fmv1992/scala_cli_parser/blob/2f1040c0ab8cc5c96f75b3ef0237991060786971/scala_cli_parser/src/main/scala/ParserWithIntermediateState.scala#L36 There's a non published branch that I'm working on but it is somewhat messy and I don't think publishing might help. – fmv1992 Jan 02 '21 at 23:00
  • Not sure what part of that line shows the problem about needing to define a constructor on a trait. Can you try to minimize and describe your problem? Also, since you are building a parser, it may be good to use an appropriate parsing library like fast-parse or cats-parse - BTW, it would be good to use an sbt-multimodule project instead of having one sbt file per folder and a docker file and a makefile. – Luis Miguel Mejía Suárez Jan 02 '21 at 23:09
  • That's not a consturctor in your question, it's a `copy` method. You can use F-bounded polymorhpism: https://stackoverflow.com/questions/15441589 Note that in that case you can't have a generic implementation in the base trait, you HAVE to provide an implementation in every overriding class. Alternatively you can try to use shapeless: https://stackoverflow.com/questions/40995639 – Kolmar Jan 02 '21 at 23:41
  • 1
    There's no point. You can't call a constructor generically. The constructors don't belong to the objects; they belong to the types. Use typeclasses/factories. – HTNW Jan 03 '21 at 02:24

0 Answers0