我试图通过API定义隐式,并希望允许客户端覆盖它们。这里是一个讨论:[How to override an implicit value, that is imported?我已经用最简单的解决方案进行了尝试。它按预期工作。现在,我想以相同的方式定义基于未来的API,并将ExecutionContext定义为具有默认值的隐式。
/**
* Client can reuse default implicit execution context or override it
*/
trait CappuccinoWithOverridableExecutionContext {
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.util.Random
import com.savdev.fp.monad.composition.future.scala.Cappuccino._
def grind(beans: CoffeeBeans)
(implicit executor:ExecutionContext = global )
: Future[GroundCoffee] = Future {
println("01.Start start grinding..., " +
"thread: " + Thread.currentThread().getName)
TimeUnit.SECONDS.sleep(Random.nextInt(3))
if (beans == "baked beans") throw GrindingException("are you joking?")
println("01.End finished grinding...")
s"ground coffee of $beans"
}(implicitly(executor))
def heatWater(water: Water)
(implicit executor:ExecutionContext = global )
: Future[Water] = Future {
println("02.Start heating the water now, " +
"thread: " + Thread.currentThread().getName)
TimeUnit.SECONDS.sleep(Random.nextInt(3))
println("02.End hot, it's hot!")
water.copy(temperature = 85)
}(implicitly(executor))
def frothMilk(milk: Milk)
(implicit executor:ExecutionContext = global )
: Future[FrothedMilk] = Future {
println("03.Start milk frothing system engaged!, " +
"thread: " + Thread.currentThread().getName)
TimeUnit.SECONDS.sleep(Random.nextInt(3))
println("03.End shutting down milk frothing system")
s"frothed $milk"
}(implicitly(executor))
def brew(coffee: GroundCoffee, heatedWater: Water)
(implicit executor:ExecutionContext = global )
: Future[Espresso] = Future {
println("04.Start happy brewing :), " +
"thread: " + Thread.currentThread().getName)
TimeUnit.SECONDS.sleep(Random.nextInt(3))
println("04.End it's brewed!")
"espresso"
}(implicitly(executor))
def combine(espresso: Espresso, frothedMilk: FrothedMilk)
(implicit executor:ExecutionContext = global )
: Future[Cappuccino.Cappuccino] = Future {
println("05.Start happy combining :), " +
"thread: " + Thread.currentThread().getName)
TimeUnit.SECONDS.sleep(Random.nextInt(3))
println("05.End it's combined!")
"cappuccino"
} (implicitly(executor))
// going through these steps asynchroniously:
def prepareCappuccinoAsynchroniously(implicit executor:ExecutionContext = global )
: Future[Cappuccino.Cappuccino] = {
println("Preparing cappucchino with overridable execution context")
val groundCoffee = grind("arabica beans")(implicitly(executor))
val heatedWater = heatWater(Water(20))(implicitly(executor))
val frothedMilk = frothMilk("milk")(implicitly(executor))
for {
ground <- groundCoffee
water <- heatedWater
foam <- frothedMilk
espresso <- brew(ground, water)(implicitly(executor))
cappuchino <- combine(espresso, foam)(implicitly(executor))
} yield cappuchino
}
}
我对理解力的每一行都犯了5个(相同)错误:
[ERROR] .../src/main/scala/com/savdev/fp/monad/composition/future/scala/CappuccinoWithOverridableExecutionContext.scala:91: error: ambiguous implicit values:
[ERROR] both lazy value global in object Implicits of type => scala.concurrent.ExecutionContext
[ERROR] and value executor of type scala.concurrent.ExecutionContext
[ERROR] match expected type scala.concurrent.ExecutionContext
[ERROR] cappuchino <- combine(espresso, foam)(implicitly(executor))
我该如何解决?它基于“隐式”关键字尝试了不同的语法,但仍然没有成功。
更新1 :
这并不是要给它论据,而是要强迫 查找给定类型的隐式。
一旦我摆脱implicitly(executor)
:
def grind(beans: CoffeeBeans)
(implicit executor:ExecutionContext = global )
: Future[GroundCoffee] = Future {
...
}(implicitly[ExecutionContext])
我遇到相同的错误:
[ERROR] .../src/main/scala/com/savdev/fp/monad/composition/future/scala/CappuccinoWithOverridableExecutionContext.scala:25: error: ambiguous implicit values:
[ERROR] both lazy value global in object Implicits of type => scala.concurrent.ExecutionContext
[ERROR] and value executor of type scala.concurrent.ExecutionContext
[ERROR] match expected type scala.concurrent.ExecutionContext
[ERROR] }(implicitly[ExecutionContext])
摆脱在executor
中显式传递prepareCappuccinoAsynchroniously
也无济于事。
@francoisr,能否请您给出一个有效的示例,导致您的建议不起作用,或者我没有正确获得建议。
更新#2。这是一个基于@Levi Ramsey和@Łukasz建议的工作版本。
/**
* Client can reuse default implicit execution context or override it
*/
trait CappuccinoWithOverridableExecutionContext {
import scala.concurrent.Future
import scala.util.Random
import com.savdev.fp.monad.composition.future.scala.Cappuccino._
//do not import it:
//import scala.concurrent.ExecutionContext.Implicits.global
val defaultEc = scala.concurrent.ExecutionContext.Implicits.global
def grind(beans: CoffeeBeans)
(implicit executor:ExecutionContext = defaultEc)
: Future[GroundCoffee] = Future {
println("01.Start start grinding..., " +
"thread: " + Thread.currentThread().getName)
TimeUnit.SECONDS.sleep(Random.nextInt(3))
if (beans == "baked beans") throw GrindingException("are you joking?")
println("01.End finished grinding...")
s"ground coffee of $beans"
}
def heatWater(water: Water)
(implicit executor:ExecutionContext = defaultEc)
: Future[Water] = Future {
println("02.Start heating the water now, " +
"thread: " + Thread.currentThread().getName)
TimeUnit.SECONDS.sleep(Random.nextInt(3))
println("02.End hot, it's hot!")
water.copy(temperature = 85)
}
def frothMilk(milk: Milk)
(implicit executor:ExecutionContext = defaultEc )
: Future[FrothedMilk] = Future {
println("03.Start milk frothing system engaged!, " +
"thread: " + Thread.currentThread().getName)
TimeUnit.SECONDS.sleep(Random.nextInt(3))
println("03.End shutting down milk frothing system")
s"frothed $milk"
}
def brew(coffee: GroundCoffee, heatedWater: Water)
(implicit executor:ExecutionContext = defaultEc )
: Future[Espresso] = Future {
println("04.Start happy brewing :), " +
"thread: " + Thread.currentThread().getName)
TimeUnit.SECONDS.sleep(Random.nextInt(3))
println("04.End it's brewed!")
"espresso"
}
def combine(espresso: Espresso, frothedMilk: FrothedMilk)
(implicit executor:ExecutionContext = defaultEc )
: Future[Cappuccino.Cappuccino] = Future {
println("05.Start happy combining :), " +
"thread: " + Thread.currentThread().getName)
TimeUnit.SECONDS.sleep(Random.nextInt(3))
println("05.End it's combined!")
"cappuccino"
}
// going through these steps synchroniously, wrong way:
def prepareCappuccinoSequentially(implicit executor:ExecutionContext = defaultEc )
: Future[Cappuccino.Cappuccino] = {
for {
ground <- grind("arabica beans")
water <- heatWater(Water(20))
foam <- frothMilk("milk")
espresso <- brew(ground, water)
cappuchino <- combine(espresso, foam)
} yield cappuchino
}
// going through these steps asynchroniously:
def prepareCappuccinoAsynchroniously(implicit executor:ExecutionContext = defaultEc)
: Future[Cappuccino.Cappuccino] = {
println("Preparing cappucchino with overridable execution context")
val groundCoffee = grind("arabica beans")
val heatedWater = heatWater(Water(20))
val frothedMilk = frothMilk("milk")
for {
ground <- groundCoffee
water <- heatedWater
foam <- frothedMilk
espresso <- brew(ground, water)
cappuchino <- combine(espresso, foam)
} yield cappuchino
}
}
答案 0 :(得分:2)
您是否考虑过不导入使它隐式的全局ExecutionContext,而只是将其绑定到值?
trait CappuccinoWithOverridableExecutionContext {
import scala.concurrent.Future
import scala.util.Random
import com.savdev.fp.monad.composition.future.scala.Cappuccino._
protected val global = scala.concurrent.ExecutionContext.Implicits.global // probably rename this to something like defaultGlobalExecutionContext
}
然后,您可以在明确隐式位置的同时使用全局上下文。我还要删除implicitly
s
答案 1 :(得分:0)
您无需在executor
中的任何地方显式传递prepareCappuccinoAsynchroniously
,因为prepareCappuccinoAsynchroniously
范围内的隐式参数将比global
导入具有更高的优先级。
implicitly
方法实际上不是关键字,而是在scala.Predef
中定义的实际方法。它是这样实现的:
def implicitly[T](implicit t: T): T = t
这并不是要为其提供参数,而是要为给定类型强制隐式查找。也就是说,如果您需要T
隐式可用,则可以使用val t = implicitly[T]
强制使用它。
在这种情况下,您根本不需要使用implicitly
,因为您声明了implicit
参数,因此已经有了它的名称。此implicitly
方法通常与上下文绑定一起使用,上下文绑定是一个密切相关的概念,但概念更高级。您可以根据自己的兴趣查找它,但这对您的问题并不重要。
尝试通过删除所有implicitly
来使隐式函数起作用。这是一个简短的示例:
def grind(beans: CoffeeBeans)(implicit executor:ExecutionContext = global): Future[GroundCoffee] = Future { ??? }(executor)
实际上,您甚至应该可以放下executor
部分并只写
def grind(beans: CoffeeBeans)(implicit executor:ExecutionContext = global): Future[GroundCoffee] = Future { ??? }
编辑:我看错了您的帖子,您需要使用import scala.concurrent.ExecutionContext.global
而不是import scala.concurrent.ExecutionContext.Implicits.global
,因为隐式导入global
会在此处引起歧义。
答案 2 :(得分:0)
以下内容演示了如何使用隐式默认上下文或提供一个默认上下文。
object EcTest extends App {
import scala.concurrent.Future
import scala.concurrent.ExecutionContext
import java.util.concurrent.Executors
val default = scala.concurrent.ExecutionContext.Implicits.global
def test(comp: String)(implicit exc: ExecutionContext = default): Future[String] = Future {
println("Using executor: " + Thread.currentThread().getName)
comp
}
test("Use default executor")
val myExecutor = ExecutionContext.fromExecutor(Executors.newSingleThreadExecutor)
test("Use custom executor")(myExecutor)
}
演示。
scalac EcTest.scala
scala EcTest
// output
Using executor: scala-execution-context-global-10
Using executor: pool-1-thread-1