scala 如何让trait的实现者定义上下文参数类型

cyvaqqii  于 2023-03-08  发布在  Scala
关注(0)|答案(1)|浏览(146)

我想在一个库中定义一个trait,其中上下文参数的类型由库的调用者用户定义。例如,它看起来像这样:

////////////////////////
// library
////////////////////////
trait Animal:
    // goal: the type of the context we pass to `make_sound` is defined by the implementor
    def make_sound()(using ctx: ???): Unit

def do_something(animal: Animal) = {
    animal.make_sound()
}

////////////////////////
// user code
////////////////////////
trait CatMakeSoundContext:
    def get_claw_length(): Int

class Cat extends Animal:
    // FAILS because this doesn't have the same signature as in `Animal`
    override def make_sound()(using ctx: CatMakeSoundContext) = {
        if ctx.get_claw_length() > 2 {
            println("scratch!")
        } else {
            println("meow")
        }
    }

class MyCatMakeSoundContext extends CatMakeSoundContext {
    override def get_claw_length(): Int = 2
}

def call_example() = {
    val cat: Cat = ...;
    val claw_context = MyCatMakeSoundContext()    
      
    // somehow set up the `claw_context` with a `given` clause
    // to be used by `animal.make_sound()`
 
    // call `do_something()`, which will end up using `claw_context`
    // as the value for the implicit `ctx` in `make_sound()`
    do_something(cat)
}

在这个例子中,我很可能使用了错误的语言特性。有没有可能以这样或那样的方式实现类似于我在这里描述的东西?我怀疑泛型可能有用,但我无法弄清楚。

oyjwcjzk

oyjwcjzk1#

您可以尝试使上下文成为类型成员

// library
trait Animal:
  type Context
  def make_sound()(using ctx: Context): Unit

def do_something(animal: Animal)(using animal.Context): Unit =
  animal.make_sound()

// user code
trait CatMakeSoundContext:
  def get_claw_length(): Int

class Cat extends Animal:
  override type Context = CatMakeSoundContext
  override def make_sound()(using ctx: Context): Unit =
    if ctx.get_claw_length() > 2
    then println("scratch!")
    else println("meow")

class MyCatMakeSoundContext extends CatMakeSoundContext:
  override def get_claw_length(): Int = 2

def call_example(): Unit =
  val cat: Cat = Cat()
  given CatMakeSoundContext = MyCatMakeSoundContext()
  do_something(cat) // meow

也可以将上下文设置为类型参数

// library
trait Animal[Context]:
  def make_sound()(using ctx: Context): Unit

def do_something[C](animal: Animal[C])(using C): Unit =
  animal.make_sound()

// user code
trait CatMakeSoundContext:
  def get_claw_length(): Int

class Cat extends Animal[CatMakeSoundContext]:
  override def make_sound()(using ctx: CatMakeSoundContext): Unit =
    if ctx.get_claw_length() > 2
    then println("scratch!")
    else println("meow")

class MyCatMakeSoundContext extends CatMakeSoundContext:
  override def get_claw_length(): Int = 2

def call_example(): Unit =
  val cat: Cat = Cat()
  given CatMakeSoundContext = MyCatMakeSoundContext()
  do_something(cat) // meow

此外,还可以将Animal设置为类型类

// library
trait Animal[T]:
  type Context
  def make_sound()(using ctx: Context): Unit

def do_something[T](t: T)(using animal: Animal[T], ctx: animal.Context): Unit =
  animal.make_sound()

// user code
trait CatMakeSoundContext:
  def get_claw_length(): Int

class Cat
given Animal[Cat] with
  override type Context = CatMakeSoundContext
  override def make_sound()(using ctx: Context): Unit =
    if ctx.get_claw_length() > 2
    then println("scratch!")
    else println("meow")

class MyCatMakeSoundContext extends CatMakeSoundContext:
  override def get_claw_length(): Int = 2

def call_example(): Unit =
  val cat: Cat = Cat()
  given CatMakeSoundContext = MyCatMakeSoundContext()
  do_something(cat) // meow

相关问题