将Scala Implicit带入库代码的调用站点

时间:2015-12-04 17:29:17

标签: scala types

我正在尝试找到一种解决方法,将用户定义的隐式隐藏到某些库代码的调用站点中。我有类似的东西

Prune

我想补充一下这样的事情

//library code
trait Fruit { def foo: Unit }

class Apple extends Fruit {
  def foo: Unit = {
    // a bunch of imperative code
  }
} 
class Pear extends Fruit // etc

我试图将其纳入范围的想法将是这样的:

// library code
trait Fruit { def foo[+A <: Fruit](implicit fi: FruitImplicit[A]): Unit }

class Apple extends Fruit {
  def foo[Apple](implicit fi: FruitImplicit[Apple]): Unit = {
    // a bunch of imperative code
    fi.aReallyImportantMethod
  }
} 
class Pear extends Fruit // etc

trait FruitImplicits[+A <: Fruit]{
  def mark: Unit = Unit // default implementation that normally would be used
}

// user code
implicit val appleImplicit: FruitImplicit[Apple] = new FruitImplicit[Apple] {
  def mark: Unit = println("YOLO")
}

我知道我遗漏了一些东西(这显然不能编译)。有没有解决的办法?我一直认为有一种方法可以将// library code class Apple extends Fruit { object FruitOps extends LowPriorityFruitImplicits with FruitImplicits[A] {} import FruitOps._ def foo[Apple](implicit fi: FruitImplicit[Apple]): Unit = { // a bunch of imperative code fi.aReallyImportantMethod } } 静态转换为FruitImplicits,这会为该特定类型带来隐含的内容。但是,这需要创建一个本身就是其子类型的类型。

或许我疯了,这是一个可怕的想法。有什么想法吗?

1 个答案:

答案 0 :(得分:1)

我认为这是一个可怕的想法,当然,但我也认为你想做的事情是可能的(如果我理解正确的话)。

一个问题是,Applefoo的{​​{1}}类型参数会影响Apple类型。如果你想引用Apple中的子类型,你需要使用 F - 有界多态的东西:

Fruit

(为了遵循副作用代码的最佳实践,我添加了一组额外的括号,但它们不是必需的。)

您的trait Fruit[F <: Fruit[F]] { def foo()(implicit fi: FruitImplicit[F]): Unit } 将如下所示:

FruitImplicit

在这里,我们提供了一个默认操作,如果用户没有为我们调用trait FruitImplicit[A <: Fruit[A]] { def mark(): Unit } object FruitImplicit { implicit def defaultAction[F <: Fruit[F]]: FruitImplicit[F] = new FruitImplicit[F] { def mark(): Unit = println("default") } } 的类型提供隐式操作,将会使用该操作。

现在您的实施将如下所示:

mark

然后:

class Apple extends Fruit[Apple] {
  def foo()(implicit fi: FruitImplicit[Apple]): Unit = {
    println("This is an apple")
    fi.mark()
  }
} 

class Pear extends Fruit[Pear] {
  def foo()(implicit fi: FruitImplicit[Pear]): Unit = {
    println("This is a pear")
    fi.mark()
  }
}

但如果用户提供隐含的......

scala> val apple = new Apple
apple: Apple = Apple@7bb35c46

scala> val pear = new Pear
pear: Pear = Pear@777bd4a2

scala> apple.foo()
This is an apple
default

scala> pear.foo()
This is a pear
default

...它将被用来代替该类型的默认值。