【发布时间】:2019-01-26 13:15:04
【问题描述】:
我正在尝试通过 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
}
}
for-comprehension 中的每一行都有 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])
明确地摆脱在prepareCappuccinoAsynchroniously 中传递executor 也无济于事。
@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
}
}
【问题讨论】:
-
为什么不能使用 scala.concurrent.ExecutionContext.Implicit.global ?我的意思是为什么你需要创建自己的 ExecutionContext?据我所知,您可以简单地导入执行上下文,这是最有效的。实际上问题是你在同一个对象中使用了多个相同类型的隐式变量。
-
@RamanMishra,我可以。背后的主要动机 - 允许客户端选择使用哪个执行上下文。要么是默认的,由 API 创建者使用,要么覆盖它。这就是我努力实现的目标。
-
然后尝试在不同的不同范围内清除上下文,我认为应该可以。现在存在隐式集合,因为您不止一次使用相同类型的隐式。
-
@RamanMishra 使用自定义
ExecutionContext有充分的理由。global由 fork-join 线程池支持,这对许多应用程序都有好处,但对于密集型 IO 则相当不利。 -
用
scala.concurrent.ExecutionContext.global(没有Implicits)替换你导入的全局ec。导入和隐式参数总是会冲突...
标签: scala overriding future for-comprehension implicits