A bit late to the party, just trying to tie a few threads together (as far as I understand the problem)…
The issue of placing the “shared” implementation parts seems to be specific to the design based on OO inheritance. As discussed in another thread, there’s a tradeoff between this and an ADT/pattern matching/type class based design, and this seems to be one aspect in favor of the latter approach. A naive translation of @curoli’s example to this style might look like this:
sealed trait Galois5Element
object Galois5Element {
case object Zero extends Galois5Element
case object One extends Galois5Element
case object Two extends Galois5Element
case object Three extends Galois5Element
case object Four extends Galois5Element
}
trait Algebra[T] {
def mult(a: T, b: T): T
}
implicit val galois5ElementAlgebra: Algebra[Galois5Element] = {
import Galois5Element._
(a: Galois5Element, b: Galois5Element) =>
(a, b) match {
case (Zero, _) => Zero
case (_, Zero) => Zero
case (One, y) => y
case (x, One) => x
case(Two, Two) => Four
case(Two, Three) => One
case(Two, Four) => Three
case (_, _) => ???
}
}
Placing the shared algebra code becomes a non-issue, and subjectively this code looks somewhat cleaner and easier to grasp to me.
But there’s the issue of future extensions - the OO approach in general makes it easy to add new data flavors in isolation, but requires shotgun surgery to existing code when adding new functionality, with the FP/ADT approach it’s exactly the other way round. If you want both, i.e. add new data cases without changing existing code in the ADT design, you’re facing the expression problem. This can be solved, but it requires a more convoluted initial design.
I’m not sure whether this is an actual drawback in this case. It probably doesn’t make much sense to add a new data case to a Galois 5 field. As for your actual code, if I understand correctly from the other thread, you want to use pattern matching with exhaustiveness checking internally in your methods, so you’ll have to fix up existing code upon adding new data cases in this “hybrid” approach, anyway. Sounds like a net plus for the ADT/pattern matching style to me - YMMV, of course.