0

I'm wondering if it is possible to mixin a Scala trait with an instance to make an anonymous instance.

basically I would to do something like that

trait Duck {
 def walk : Unit
}

def learnToDuckWalk[A](a : A) : A with Duck = a with Duck {
 def walk = print("flic flak floc")
}

The idea is to make a new type without having to define a all new class that extends 2 types. Because define a new class for each type could lead to exponential number of class, growing each time we add a new Trait to mixin.

I saw few things that look like this in ZIO.Has but I'm wondering if it is doable with pure Scala 2 or even with Scala 3.

crak
  • 1,635
  • 2
  • 17
  • 33
  • 2
    This is exactly what ad-hoc-polymorphism looks like. And type-classes are the way to go for these use-cases. – sarveshseri Sep 27 '21 at 10:24
  • I could see someone doing this with a macro, but could you please give an example of how ZIO does it, so the use case is clearer? – user Sep 27 '21 at 21:17

1 Answers1

1

Do you really need a generic parameter A, or was that just for example sake? If you had a concrete type instead of A, then you could just create an instance that mixes in both traits:

trait Duck {
  def walk: Unit
}

trait Goose {
  def run: Unit
}

def learnToDuckWalk: Goose with Duck = new Goose with Duck {
  def run = println("tip tap top")
  def walk = println("flic flak floc")
}

learnToDuckWalk.walk
learnToDuckWalk.run

However, having a generic A and "bolting-on" some extra traits is not really a good design, even if you managed to implement it (e.g. using reflection).

A better approach would be, instead of saying "I have an A that is mixed in with Duck", to say "I have an A that has a Duck type class instance".

trait Duck [A]{
  def walk : Unit
}

object Duck {

  implicit val duckString = new Duck[String] {
    def walk = print("Stringy flic flak floc")
  }

  implicit val duckInt = new Duck[Int] {
    def walk = print("Inty flic flak floc")
  }

  ...

  implicit def duckA[A] = new Duck[A] {
    def walk = print("Generic flic flak floc")
  }
}

This way you can provide type class instances for any type you want, and you can have them behave differently depending on the actual type (see how walk prints a different message fo each type).

Then your use-site can look like this:

import Duck._

def learnToDuckWalk[A : Duck](a : A) = {
  val aDuck = implicitly[Duck[A]]
  aDuck.walk // prints Stringy flic flak floc
}

learnToDuckWalk("Some string")

Important part here is the [A : Duck] type, which means "any A for which a Duck type class instance exists".

slouc
  • 9,508
  • 3
  • 16
  • 41
  • This doesn't solve my probem, I don't need a type class but one type which has all abilities, because I would like to pass them in a monad reader. Imagine needing a Duck with Sheep with Cow with Chicken, in this case the first solution would make way to much instances. – crak Sep 27 '21 at 10:03
  • 1
    If you use cake pattern or ZIO for dependency management, then that's exactly what you would do - have a set of dependencies defined as `Cow with Duck with Sheep`. But you wouldn't need to "bolt" them on. Instead, you would just need to provide them one by one (ZIO layers can help with that). – slouc Sep 27 '21 at 10:18
  • Yes, I was wondering if that was possible to have the ZIO Has functionality without pulling all ZIO. I saw another post about the subject : https://stackoverflow.com/questions/61135323/scala-dotty-mix-a-trait-into-an-existing-object The basic idea is to pass information to the "instance" layer of a tagless final program without having that shown in the "program" layer. Usually I create instance directly with the information needed. But sometime I need something more dynamic that must have different value with the same instance. – crak Sep 28 '21 at 09:43
  • I see. I thought it was more of a beginner question. Yeah as you surely realized by now, for cramping some additional traits dynamically onto an already constructed object, you definitely need magic (for me "magic" usually means macros and/or reflection). I would still go for type classe, especially considering that you're using tagless final. Something like `def foo[A : Cow : Duck : Sheep]`. But yes, for the ability to "plug in" instances and not having them visible in the type from that point onwards (like partially applying a function), I would go for ZLayers. – slouc Sep 28 '21 at 11:24