Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prohibit generating of apply for case class

I'm writing a type-safe code and want to replace apply() generated for case classes with my own implementation. Here it is:

import shapeless._

sealed trait Data
case object Remote extends Data
case object Local extends Data

case class SomeClass(){
  type T <: Data
}

object SomeClass {
  type Aux[TT] = SomeClass { type T = TT }
  def apply[TT <: Data](implicit ev: TT =:!= Data): SomeClass.Aux[TT] = new SomeClass() {type T = TT}
}

val t: SomeClass = SomeClass() // <------------------ still compiles, bad
val tt: SomeClass.Aux[Remote.type] = SomeClass.apply[Remote.type] //compiles, good
val ttt: SomeClass.Aux[Data] = SomeClass.apply[Data] //does not compile, good

I want to prohibit val t: SomeClass = SomeClass() from compiling. Is it possible to do somehow except do not SomeClass to be case class?

like image 333
Some Name Avatar asked Oct 20 '20 21:10

Some Name


Video Answer


1 Answers

There is a solution that is usually used if you want to provide some smart constructor and the default one would break your invariants. To make sure that only you can create the instance you should:

  • prevent using apply
  • prevent using new
  • prevent using .copy
  • prevent extending class where a child could call the constructor

This is achieved by this interesing patten:

sealed abstract case class MyCaseClass private (value: String)
object MyCaseClass {
  def apply(value: String) = {
    // checking invariants and stuff
    new MyCaseClass(value) {}
  }
}

Here:

  • abstract prevents generation of .copy and apply
  • sealed prevents extending this class (final wouldn't allow abstract)
  • private constructor prevents using new

While it doesn't look pretty it's pretty much bullet proof.

As @LuisMiguelMejíaSuárez pointed out this is not necessary in your exact case, but in general that could be used to deal with edge cases of case class with a smart constructor.

like image 118
Mateusz Kubuszok Avatar answered Sep 25 '22 20:09

Mateusz Kubuszok