Scala:用于理解的可重写隐式
我试图通过API定义隐式,并希望允许客户端重写它们。下面是一个讨论:[我已经用最简单的解决方案尝试过了。它可以按预期工作。现在我想用相同的方式定义基于未来的API,ExecutionContext定义为隐式,并带有默认值Scala:用于理解的可重写隐式,scala,overriding,future,for-comprehension,implicits,Scala,Overriding,Future,For Comprehension,Implicits,我试图通过API定义隐式,并希望允许客户端重写它们。下面是一个讨论:[我已经用最简单的解决方案尝试过了。它可以按预期工作。现在我想用相同的方式定义基于未来的API,ExecutionContext定义为隐式,并带有默认值 /** * Client can reuse default implicit execution context or override it */ trait CappuccinoWithOverridableExecutionContext { import
/**
* 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
}
}
我得到了同样的错误:
[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])
在preparecappuccinoasynchronous
中清除传递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
}
}
/**
*客户端可以重用默认的隐式执行上下文或重写它
*/
trait CappuccinoWithOverridableExecutionContext{
导入scala.concurrent.Future
导入scala.util.Random
导入com.savdev.fp.monad.composition.future.scala.Cappuccino_
//不要导入它:
//导入scala.concurrent.ExecutionContext.Implicits.global
val defaultEc=scala.concurrent.ExecutionContext.Implicits.global
def研磨(咖啡豆:咖啡豆)
(隐式执行器:ExecutionContext=defaultEc)
:Future[磨碎的咖啡]=Future{
println(“01.开始研磨…”+
“线程:”+thread.currentThread().getName)
时间单位。秒。睡眠(随机。nextInt(3))
如果(beans==“bakedbeans”)抛出GrindingException(“你在开玩笑吗?”)
println(“01.结束研磨…”)
s“豆磨咖啡”
}
def热水(水:水)
(隐式执行器:ExecutionContext=defaultEc)
:未来[水]=未来{
println(“02.现在开始加热水,”+
“线程:”+thread.currentThread().getName)
时间单位。秒。睡眠(随机。nextInt(3))
println(“02.End热,很热!”)
水。复印件(温度=85)
}
def泡沫牛奶(牛奶:牛奶)
(隐式执行器:ExecutionContext=defaultEc)
:Future[泡沫牛奶]=Future{
println(“03.启动牛奶起泡系统已启用!”+
“线程:”+thread.currentThread().getName)
时间单位。秒。睡眠(随机。nextInt(3))
println(“03.End关闭牛奶起泡系统”)
s“泡沫牛奶”
}
def冲泡(咖啡:磨咖啡,热水:水)
(隐式执行器:ExecutionContext=defaultEc)
:Future[浓缩咖啡]=Future{
println(“04.Start happy brewing:),”+
“线程:”+thread.currentThread().getName)
时间单位。秒。睡眠(随机。nextInt(3))
println(“04.End,它已经煮好了!”)
“浓缩咖啡”
}
def组合咖啡(浓缩咖啡:浓缩咖啡,泡沫牛奶:泡沫牛奶)
(隐式执行器:ExecutionContext=defaultEc)
:Future[卡布奇诺.卡布奇诺]=Future{
println(“05.开始快乐组合:),”+
“线程:”+thread.currentThread().getName)
时间单位。秒。睡眠(随机。nextInt(3))
println(“05.End它被合并了!”)
“卡布奇诺”
}
//以错误的方式同步执行这些步骤:
def preparecappuccinosequentily(隐式执行器:ExecutionContext=defaultEc)
:Future[Cappuccino.Cappuccino]={
为了{
地面您不需要在preparecappuccinoasynchronously
中的任何位置显式传递executor
,因为preparecappuccinoasynchronously
范围内的隐式参数将优先于全局导入
隐式
方法实际上不是一个关键字,而是在scala.Predef
中定义的一个实际方法。它的实现如下:
def implicitly[T](implicit t: T): T = t
它不是要被赋予它的参数,而是要强制查找给定类型的隐式。也就是说,如果需要隐式提供T
,可以使用val T=implicitly[T]
强制它
在您的案例中,您根本不需要隐式地使用
,因为您声明了一个隐式
参数,因此您已经有了它的名称。此隐式
方法通常与上下文绑定一起使用,这是一个密切相关但稍微高级的概念。您可以在您感兴趣的网站中查找它,但它没有这对你的问题并不重要
尝试通过隐式删除任何
,让隐式完成工作。下面是一个简短的示例:
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
会在这里造成歧义。你不需要显式传递executer
任何地方e> PrepareCapuccinoAsynchronously
,因为PrepareCapuccinoAsynchronously
范围内的隐式参数将优先于全局导入
隐式
方法实际上不是一个关键字,而是在scala.Predef
中定义的一个实际方法。它的实现如下:
def implicitly[T](implicit t: T): T = t
它不是要被赋予它的参数,而是要强制查找给定类型的隐式。也就是说,如果需要隐式提供T
,可以使用val T=implicitly[T]
强制它
在您的案例中,您根本不需要隐式地使用
,因为您声明了一个隐式
参数,因此您已经有了它的名称。此隐式
方法通常与上下文绑定一起使用,这是一个密切相关但稍微高级的概念。您可以在您感兴趣的网站中查找它,但它没有这对你的问题并不重要
尝试通过隐式删除任何
,让隐式完成工作。下面是一个简短的示例:
def grind(beans: CoffeeBeans)(implicit executor:ExecutionContext = global): Future[GroundCoffee] = Future { ??? }(executor)
实际上,您甚至应该能够删除executor
部分,只编写
def grind(beans: CoffeeBeans)(implicit executor:ExecutionContext = global): Future[GroundCoffee] = Future { ??? }
编辑:我看错了你的pos
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