简体   繁体   English

在Scala中组合类型界限

[英]Combining type bounds in Scala

Let's say I have a function such as: 假设我有一个函数,例如:

def foo[T<:U <:V](t:T): Unit

I want to know if there is a way of combining these two in a type W so that I can have: 我想知道是否有一种将两种类型合并为W这样我就可以拥有:

def foo[T<: W](t: T): Unit

The use case for this is: 用例是:

trait FooTrait{
   type W
   def foo[T<: W](t: T): Unit
}

I might have two different implementations of foo, one of them with a simple W1 type bound whereas the other one is T<:W1 <:W2 I want the more complex one to define a W3 so that I can have: 我可能有两种不同的foo实现,其中一种实现了一个简单的W1类型绑定,而另一种实现是T<:W1 <:W2我想要更复杂的一种来定义W3这样我就可以拥有:

def foo[T<: W3](t: T): Unit

Similiarly, I want to be able to do these with type classes. 同样,我希望能够使用类型类来实现这些功能。 So if I have: 所以,如果我有:

def bar[T<:U :V](t:T): Unit

I want to have 我希望有

def bar[T:X](t:T): Unit

The use case for this is essentially the same as the earlier case. 此用例与先前的用例基本相同。

is this possible? 这可能吗?

In the first part of your question, the syntax isn't even valid. 在问题的第一部分,语法甚至无效。 If you want to impose multiple upper bounds U , V on some type T , you have to use with keyword anyway: 如果你想强加给多个上限UV对某些类型的T ,你必须使用with反正关键字:

trait A
trait B

def f[X <: A with B](x: X): Unit = ???

This here doesn't work: 这在这里不起作用:

// def f[X <: A <: B](x: X): Unit = ??? // doesn't compile

To address the second part of your question, I would like to explain why something like with doesn't work for typeclasses. 为了解决您的问题的第二部分,我想解释一下为什么with类的东西对类型类不起作用。

This here does work: 这在这里起作用:

trait Foo[X]
trait Bar[X]

def g[X: Foo : Bar](x: X): Unit = ???

This here doesn't work: 这在这里不起作用:

// def g[X: Foo with Bar](x: X): Unit = ??? // nope

Why? 为什么? Because 因为

def foo[X: Y](x: X): Ret = ???

is actually a shortcut for 实际上是

def foo[X](x: X)(implicit y: Y[X]): Ret = ???

If you would try to somehow amalgamate two different typeclasses Foo and Bar , this would result in the following desugared code: 如果您尝试以某种方式合并两个不同的类型类FooBar ,则将导致以下简化的代码:

def foo[X](x: X)(implicit superPowerfulThing: (Foo somehowGluedWith Bar)[X]): Ret = ???

But this is obviously not something that you should want. 但这显然不是您应该想要的。 Instead, what you want is: 相反,您想要的是:

def foo[X](x: X)(implicit foo: Foo[X], bar: Bar[X])(x: X): Ret = ???

In this way, the two requirements Foo and Bar can be supplied independently, which wouldn't work if you requested some superPowerfulThing that implements both Foo and Bar at once. 这样,可以单独提供FooBar这两个需求,如果您请求一次同时实现FooBar superPowerfulThing ,则这是superPowerfulThing

Hope that clarifies why something works or doesn't work. 希望能弄清楚为什么某些东西起作用或不起作用。

Andrey Tyukin has correctly point out that with is probably the answer for your first question and that the second question has no direct solution. Andrey Tyukin正确指出,“ with可能是您第一个问题的答案,而第二个问题没有直接解决方案。 However if you are OK with indirect solutions, you might work this around by introducing a new type class that will says that the target types belongs to the two original type classes. 但是,如果您可以使用间接解决方案,则可以通过引入一个新的类型类来解决此问题,该类将指出目标类型属于两个原始类型类。 Here is a simple example for imaginary typeclasses Foo and Bar and for a base trait Base with a specific implementation ChildFooBar : 这是虚拟类型类FooBar以及具有特定实现ChildFooBar的基本特征Base的简单示例:

trait Foo[X] {
  def doFoo(x: X): String
}

trait Bar[X] {
  def doBar(x: X): String
}

trait Base {
  type W[_]

  def baz[T: W](t: T): String
}

class ChildFooBar extends Base {

  import ChildFooBar._

  type W[X] = FooAndBar[X]


  override def baz[T: FooAndBar](t: T): String = {
    val foo = implicitly[Foo[T]]
    val bar = implicitly[Bar[T]]
    foo.doFoo(t) + bar.doBar(t)
  }
}

object ChildFooBar {

  @implicitNotFound("The type should provide both implicit Foo and implicit Bar.")
  case class FooAndBar[X](foo: Foo[X], bar: Bar[X])

  object FooAndBar {
    implicit def fromFooAndBar[X](implicit foo: Foo[X], bar: Bar[X]): FooAndBar[X] = FooAndBar(foo, bar)
  }

  // private is important here to avoid diversion of implicits
  private implicit def toFoo[X](implicit fooAndBar: FooAndBar[X]): Foo[X] = fooAndBar.foo
  private implicit def toBar[X](implicit fooAndBar: FooAndBar[X]): Bar[X] = fooAndBar.bar

}

Now if SomeClass is a member of both Foo and Bar typeclasses, then 现在,如果SomeClassFooBar类型类的成员,则

case class SomeClass(foo: Int, bar: Double)

object SomeClass {
  implicit val foo: Foo[SomeClass] = new Foo[SomeClass] {
    override def doFoo(x: SomeClass) = s"foo = ${x.foo}"
  }
  implicit val bar: Bar[SomeClass] = new Bar[SomeClass] {
    override def doBar(x: SomeClass) = s"bar = ${x.bar}"
  }
}

the simple code 简单的代码

println(new ChildFooBar().baz(SomeClass(1, 2.0)))

will compile and work as expected. 将按预期进行编译和工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM