我正在尝试获取任意
Future
元组并返回已完成的 future 值的元组,同时提供完成 future 的时间限制。我正在尝试使用 Tuple
提供的 Map
匹配类型:
def getAll[T <: Tuple](futures: Tuple.Map[T, Future])(timeout: Long, units: TimeUnit): T = futures match
case e: EmptyTuple => EmptyTuple.asInstanceOf[T]
case fs: (fh *: ft) => // here, I'd think `fh` would be known to be a Future, specifically Head[Map[T, Future]], and `ft` a Map[Tail[T], Future]
val start = System.currentTimeMillis()
val vh = fs.head.asInstanceOf[fh].get(timeout, units)
val elapsed = System.currentTimeMillis() - start
val remaining = TimeUnit.MILLISECONDS.convert(timeout, units) - elapsed
vh *: getAll(fs.tail)(remaining)
但我收到错误:
value get is not a member of fh
where: fh is a type in method getAll with bounds
val vh = fs.head.asInstanceOf[fh].get(timeout, units)
编译器似乎无法判断
fh
是 Future
。我正在尝试遵循从我的上一个问题中获得的有关匹配类型的指导,特别是尝试将值模式与匹配类型模式相匹配,但我想仍然缺少一些东西。
有什么想法吗?
编辑: 我得到的这个版本至少可以编译并且似乎可以正常运行:
def getAll[T <: Tuple](futures: Tuple.Map[T, Future])(timeout: Long, units: TimeUnit): T = futures match
case _: EmptyTuple => EmptyTuple.asInstanceOf[T]
case fs: Tuple.Map[fh *: ft, Future] =>
val start = System.nanoTime()
val vh = fs.head.asInstanceOf[Future[fh]].get(timeout, units)
val elapsed = System.nanoTime() - start
val remaining = TimeUnit.NANOSECONDS.convert(timeout, units) - elapsed
(vh *: getAll(fs.tail)(remaining, TimeUnit.NANOSECONDS)).asInstanceOf[T]
但是a)带有警告:
scala.Tuple.Map[ft, java.util.concurrent.Future]
) @ft @fh cannot be checked at runtime
case fs: Tuple.Map[fh *: ft, Future] =>
这是有道理的,我只是不知道如何解决它。我猜我是否能以某种方式变出一个
ClassTag[ft]
,但这似乎不可能......
b) 用法需要类型归属,这使得它的可用性大大降低,例如:
getAll[(String, String)]((f1, f2))(to, TimeUnit.SECONDS) // doesn't compile without [(String, String)]
斯卡斯蒂这里
在匹配类型中,
case Tuple.Map[fh *: ft, Future]
是有意义的。但在模式匹配中,由于类型擦除,case fs: Tuple.Map[fh *: ft, Future]
只是case fs: Tuple.Map[_, _]
。
目前在价值层面匹配类型效果不太好(很多事情无法推断)。好的旧类型课程可以更好。
我猜你的意思是
Await.result
Future.get
。summonFrom { _: some_evidence => ... }
import scala.compiletime.summonFrom
import scala.concurrent.{Await, Future}
import scala.concurrent.duration.{*, given}
import scala.concurrent.ExecutionContext.Implicits.given
inline def getAll[T <: Tuple](futures: Tuple.Map[T, Future])(
timeout: Long,
units: TimeUnit
): T = inline futures match
case _: EmptyTuple =>
summonFrom {
case _: (EmptyTuple =:= T) => EmptyTuple
}
case vfs: (fh *: ft) =>
vfs match
case vfh *: vft =>
val start = System.currentTimeMillis()
summonFrom {
case _: (`fh` <:< Future[h]) =>
val vh: h = Await.result(vfh, Duration(timeout, units))
val elapsed = System.currentTimeMillis() - start
val remaining = MILLISECONDS.convert(timeout, units) - elapsed
summonFrom {
case _: (Tuple.InverseMap[`ft`, Future] =:= t) =>
summonFrom {
case _: (`ft` =:= Tuple.Map[`t` & Tuple, Future]) =>
summonFrom {
case _: ((`h` *: `t`) =:= T) =>
vh *: getAll[t & Tuple](vft)(remaining, units)
}
}
}
}
测试:
getAll[(Int, String)](Future(1), Future("a"))(5000, MILLISECONDS) // (1,a)
也许用
getAll
来定义 Tuple.InverseMap
更好(根本不用 Tuple.Map
)
inline def getAll[T <: Tuple](futures: T)(
timeout: Long,
units: TimeUnit
): Tuple.InverseMap[T, Future] = inline futures match
case _: EmptyTuple =>
summonFrom {
case _: (EmptyTuple =:= Tuple.InverseMap[T, Future]) => EmptyTuple
}
case vfs: (fh *: ft) =>
vfs match
case vfh *: vft =>
val start = System.currentTimeMillis()
summonFrom {
case _: (`fh` <:< Future[h]) =>
val vh: h = Await.result(vfh, Duration(timeout, units))
val elapsed = System.currentTimeMillis() - start
val remaining = MILLISECONDS.convert(timeout, units) - elapsed
summonFrom {
case _: ((`h` *: Tuple.InverseMap[`ft`, Future]) =:= (Tuple.InverseMap[T, Future])) =>
vh *: getAll[ft](vft)(remaining, units)
}
}
测试:
getAll(Future(1), Future("a"))(5000, MILLISECONDS) // (1,a)
现在您不需要在调用站点指定
getAll
的类型参数。
更容易的是在类型级别(数学类型)和值级别(模式匹配)上递归地定义
getAll
。那么你就不需要隐式提示了
type GetAll[T <: Tuple] <: Tuple = T match
case EmptyTuple => EmptyTuple
case Future[h] *: ft => h *: GetAll[ft]
inline def getAll[T <: Tuple](futures: T)(
timeout: Long,
units: TimeUnit
): GetAll[T] = inline futures match
case _: EmptyTuple => EmptyTuple
case vfs: (Future[_] *: ft) =>
vfs match
case vfh *: vft =>
val start = System.currentTimeMillis()
val vh = Await.result(vfh, Duration(timeout, units))
val elapsed = System.currentTimeMillis() - start
val remaining = MILLISECONDS.convert(timeout, units) - elapsed
vh *: getAll[ft](vft)(remaining, units)
请注意,如果将
GetAll
的递归定义替换为
type GetAll[T <: Tuple] = Tuple.InverseMap[T, Future]
代码停止编译。您必须再次添加隐式提示。
我提醒您比赛类型的规则:
这种匹配表达式的特殊输入模式仅在满足以下条件时使用:
- 匹配表达式模式没有防护
- 匹配表达式受审者类型是匹配类型受审者类型的子类型
- 匹配表达式和匹配类型具有相同的事例数
- 匹配表达式模式都是 Typed Patterns,这些类型是
到它们在匹配中对应的类型模式 类型=:=
如果我们专门化类型参数并引入类型别名,编译器似乎无法识别伴随模式匹配定义的匹配类型定义:
type A[T] = T match
case Int => Double
case String => Boolean
def foo[T](t: T): A[T] = t match
case _: Int => 1.0
case _: String => true
编译并
type A[T] = T match
case Int => Double
case String => Boolean
type B[T] = A[T]
def foo[T](t: T): B[T] = t match
case _: Int => 1.0
case _: String => true
确实并且
type A[T, F[_]] = T match
case Int => Double
case String => Boolean
def foo[T](t: T): A[T, Option] = t match
case _: Int => 1.0
case _: String => true
但是
type A[T, F[_]] = T match
case Int => Double
case String => Boolean
type B[T] = A[T, Option]
def foo[T](t: T): B[T] = t match
case _: Int => 1.0
case _: String => true
没有(Scala 3.2.2)。案例的顺序也很重要:
type A[T] = T match
case Int => Double
case String => Boolean
def foo[T](t: T): A[T] = t match
case _: String => true
case _: Int => 1.0
无法编译。
所以最简单的实现是
inline def getAll[T <: Tuple](futures: T)(
timeout: Long,
units: TimeUnit
): Tuple.InverseMap[T, Future] = inline futures match
case vfs: (Future[_] *: ft) =>
vfs match
case vfh *: vft =>
val start = System.currentTimeMillis()
val vh = Await.result(vfh, Duration(timeout, units))
val elapsed = System.currentTimeMillis() - start
val remaining = MILLISECONDS.convert(timeout, units) - elapsed
vh *: getAll[ft](vft)(remaining, units)
case _: EmptyTuple => EmptyTuple
这就是
Tuple.InverseMap
定义中的情况顺序 https://github.com/lampepfl/dotty/blob/3.2.2/library/src/scala/Tuple.scala#L184-L187
另请参阅