Home > OS >  Use one recursive case class constructor in two sealed traits
Use one recursive case class constructor in two sealed traits

Time:11-07

I am defining few ADTs representing logic formulas. They all use i.e. And constructor, but then differ in what other constructors they use. I'd like to reuse case class definitions with a hope that I could reuse some code later. I'd like to do something like:

sealed trait Formula
selaed trait PositiveFormula

case class Not(sub) extends Formula
case class And(left, right) extends Formula, PositiveFormula

but this doesn't work for any single type for sub, left and right. So I'd like to say:

sealed trait Formula
selaed trait PositiveFormula

case class Not[A](sub : A)
Not[Formula] extends Formula
case class And(left : A, right : A)
And[Formula] extends Formula
And[PositiveFormula] extends PositiveFormula

few questions:

  1. Is anything like above possible and I just dont know syntax?
  2. Is there other solution to "reuse case class constructors" problem?
  3. Whats your opinion on how useful this would be if possible?

CodePudding user response:

You need types for case class fields. You can probably make PositiveFormula extend Formula too. For example, this would work for Boolean operands.

sealed trait Formula
sealed trait PositiveFormula extends Formula
case class Not(sub:Boolean) extends Formula
case class And(left:Boolean, right:Boolean) extends PositiveFormula

CodePudding user response:

In Scala parametric classes can't extend different parents for different type parameters (only parent's type parameter can vary).

It seems you want to have something like "Not[T] extends T", "And[T] extends T" (pseudocode). See Scala : class[T] extends T?

You can try

sealed trait Formula
sealed trait PositiveFormula

trait NotLike[A] {
  def sub: A

  // your generic code here
}

case class Not(sub: Formula) extends NotLike[Formula] with Formula

trait AndLike[A] {
  def left: A
  def right: A

  // your generic code here
}

case class And(left: Formula, right: Formula) extends 
  AndLike[Formula] with Formula

case class PositiveAnd(left: PositiveFormula, right: PositiveFormula) extends 
  AndLike[PositiveFormula] with PositiveFormula

You can also introduce FormulaLike, a common parent for Formula and PositiveFormula

sealed trait FormulaLike
sealed trait Formula extends FormulaLike
sealed trait PositiveFormula extends FormulaLike

trait NotLike[A <: FormulaLike] {
  def sub: A
}

trait AndLike[A <: FormulaLike] {
  def left: A
  def right: A
}

Is PositiveFormula a Formula or not? Just in case, if so then you can make PositiveFormula extend Formula (instead of introducing FormulaLike).

Or maybe you can try to express your relations between types with type classes (inheritance can be too restrictive, composition should be preferred over inheritance)

https://www.baeldung.com/scala/type-classes

https://kubuszok.com/2018/implicits-type-classes-and-extension-methods-part-1/

https://tpolecat.github.io/2015/04/29/f-bounds.html

https://github.com/milessabin/shapeless/blob/main/core/shared/src/main/scala/shapeless/ops/nat.scala

// hierarchy
sealed trait Formula
  
// type class
trait IsPositive[A <: Formula]

case class Not[A <: Formula](sub: A) extends Formula
  
case class And[A <: Formula](left: A, right: A) extends Formula
implicit def and[A <: Formula](implicit ev: IsPositive[A]): IsPositive[And[A]] = null
  • Related