How to abstract over one property of a trait but keep all other aspects as is?

I would like to do the following somehow and wonder how far I can get with Scala:

trait Generator[A] {
  def generate(): LazyList[A]
}
trait RandomGenerator[T] extends Generator[T]
trait OrderedGenerator[T] extends Generator[T]{
  val size: Int
}

class TransformGenerator[T, R, G[A] <: Generator[A]](
  private val gt: G[T], 
  private val transform: (T) => R
) extends G[R] { // G[R] is not a type class, => maybe somehow possible with a macro, generating a class on the fly?

  // double definition, I don't know how to say all but generate or another way so that generate below takes precedence
  export gt.*; 

   override def generate(): LazyList[R] = gt.generate().map(transform)
}
1 Like

This gives me flashbacks to writing C++ template class definitions, which did allow precisely this - and a whole lot of really messy error messages and subtle issues about duplication of compiled code segments into the bargain. :cat_with_wry_smile:

Sorry, I can’t answer your question, but let me ask you this anyway: when writing in Java, C#, F# and Scala, none of which allow this kind of ā€˜generic boilerplate API inheritance’, I’ve never felt the lack of this capability.

If you have your TransformGenerator extend Generator[R], then isn’t that enough for client code to use it?

I see you are already allowing flexibility in the underlying generation method by delegating to an existing core generator implementation and transforming that.

(You could btw just treat that transform as a map operation method in the Generator API. This is what Scalacheck and Americium do.)

Anyway, some inspiration here: americium/src/main/scala/com/sageserpent/americium/Trials.scala at 292e5ad3b082211c7d124061f23a4e69ad5b509c Ā· sageserpent-open/americium Ā· GitHub

Have a look around to see how it works under the bonnet. I wrote some design notes too: Design and Implementation Ā· sageserpent-open/americium Wiki Ā· GitHub

Hope this is useful in some way.

EDIT: I forgot to ask, was an LLM involved on writing this? Indulge me, it’s a phase I’m going through asking this question every now and then…

I’d use inheritance for data and intrinsic functionality and otherwise use typeclasses. The un-nice thing about typeclasses is that you have more boilerplate, but the nice thing is that you can resolve ā€œnotā€-style relationships as part of context parameter resolution, so you can have ā€œforward everything exceptā€ by writing only the ā€œexceptā€ error instead of manually forwarding everything else.

Here’s a runnable example: Scastie - An interactive playground for Scala.

@sageserpent-open No, not even considered to use an LLM for this question as I doubt it would give me good answers. Right, in C++ it would be possible but I am also happy to do without the cryptic error messages :smiley: Thanks for the link, I’ll check it out. I already provide map, filter and co. I write the whole thing in Kotlin and from time to time I wonder if using Scala would allow me to reduce more code duplication. So in this case I was wondering if I can reduce 3 tiny classes to one which basically do the same. I am not proficient in writing macros nor typeclasses and was curious…

@Ichoran thanks for your example, I wasn’t aware of that typeclasses allow to define such not-style relationships :slight_smile: For this particular case (like export all but… ) it seems overly involved to me, maybe worth adding something to the export clause construct. Or allow that an override in the class takes precedence but that’s more for the contributors forum I guess.