crak
crak

Reputation: 1665

Scala mix with instance

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.

Upvotes: 0

Views: 508

Answers (1)

slouc
slouc

Reputation: 9698

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".

Upvotes: 1

Related Questions