【问题标题】:Scala context boundsScala 上下文边界
【发布时间】:2017-02-01 08:39:29
【问题描述】:

在 scala 中使用上下文边界,您可以做类似的事情

trait HasBuild[T] {
  def build(buildable: T): Something
}

object Builders {
  implict object IntBuilder extends HasBuild[Int] {
    override def build(i: Int) = ??? // Construct a Something however appropriate
  }
}

import Builders._
def foo[T: HasBuild](input: T): Something = implicitly[HasBuild[T]].build(1)
val somethingFormInt = foo(1)

或者干脆

val somethingFromInt = implicitly[HasBuild[Int]].build(1)

我如何表达在范围内具有适当隐式HasBuild 对象的任何元素的Seq 的类型?如果没有太多的魔法和外部库,这可能吗?

Seq[WhatTypeGoesHere] - 我应该能够为每个元素找到合适的HasBuild

这显然不能编译:

val buildables: Seq[_: HasBuild] = ???

【问题讨论】:

  • 您的示例未在任何地方使用上下文绑定。它使用隐式。
  • 你是对的,更新了。如果您知道我正在尝试做的事情的名称,也可以随时更新标题。
  • 你的意思是存在型吗?喜欢val buildables: Seq[HasBuild[_]] = Seq(IntBuilder, SomeStringBuilder)
  • 不。我想要一个Seq of anything,其范围内有一个隐含的HasBuild
  • 所以:def foo[T: HasBuild](input: Seq[T])?

标签: scala context-bound


【解决方案1】:

基本上,我希望能够以通用方式(例如:构建)处理不相关的类型,而无需用户手动将它们包装在某种适配器中 - 并由编译器强制执行,这些类型实际上可以被处理.不知道目的是否明确。

你可以做的事情:

case class HasHasBuild[A](value: A)(implicit val ev: HasBuild[A])
object HasHasBuild {
  implicit def removeEvidence[A](x: HasHasBuild[A]): A = x.value
  implicit def addEvidence[A: HasBuild](x: A): HasHasBuild[A] = HasHasBuild(x)
}

现在(假设您添加 HasBuild[String] 进行演示):

val buildables: Seq[HasHasBuild[_]] = Seq(1, "a")

编译,但是

val buildables1: Seq[HasHasBuild[_]] = Seq(1, "a", 1.0)

没有。当您只有 HasHasBuild 时,您可以使用带有隐式 HasBuild 参数的方法:

def foo1[A](x: HasHasBuild[A]) = {
  import x.ev // now you have an implicit HasBuild[A] in scope
  foo(x.value)
}

val somethings: Seq[Something] = buildables.map(foo1(_))

【讨论】:

  • 它实际上是不完整的。我已经对其进行了扩展,以展示您将如何调用像 foo 这样的方法。
【解决方案2】:

首先,与某些 cmets 不同,您依赖于上下文边界。为 T 请求隐式类型类实例就是所谓的“上下文绑定”。

你想要的是可以实现的,但不是微不足道的,当然也不能没有其他库。

import shapeless.ops.hlist.ToList
import shapeless._
import shapeless.poly_

object builder extends Poly1 {
  implicit def caseGeneric[T : HasBuilder] = {
    at[T](obj => implicitly[HasBuilder[T]].build(obj))
  }
}

class Builder[L <: HList](mappings: L) {
  def build[HL <: HList]()(
    implicit fn: Mapper.Aux[builder.type, L, HL],
    lister: ToList[Something]
  ) = lister(mappings map fn)

  def and[T : HasBuilder](el: T) = new Builder[T :: L](el :: mappings)
}


object Builder {
  def apply[T : HasBuilder](el: T) = new Builder(el :: HNil)
}

现在您可能可以执行以下操作:

Builder(5).and("string").build()

这将从所有单独的隐式类型类实例中调用build 方法,并为您提供结果列表,其中每个结果的类型为Something。它依赖于所有构建方法都具有Something 的下限这一事实,例如根据您的示例:

trait HasBuild[T] {
  def build(buildable: T): Something
}

【讨论】:

  • 谢谢,但我想不依赖它。 @Alexey 建议的解决方案将所有内容包装在一个案例类中,但它通过隐式自动发生,所以对我来说没问题。
  • @bali182 我同意,它更干净;我不知道你能做到这一点
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-01
  • 2020-07-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多