scala 3 map tuple to futures of tuple types and back - scala

I'm trying to take an arbitrary tuple of Futures and return a tuple of the completed future's values, while providing a time limit for the completion of the futures. I'm trying to use Tuple's provided Map match type:
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)
But I'm getting an error:
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)
It seems like the compiler can't tell that fh is a Future. I'm trying to follow guidance on match types I got from a previous question of mine, in particular trying to match the value patterns with the match type patterns, but I guess am still missing something.
Any ideas?
EDIT:
I got to this version that at least compiles and seems to run properly:
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]
but a) with the warning:
scala.Tuple.Map[ft, java.util.concurrent.Future]
) #ft #fh cannot be checked at runtime
case fs: Tuple.Map[fh *: ft, Future] =>
which makes sense, I just don't know how to fix it. I'm guessing if I could somehow conjure a ClassTag[ft], but that doesn't seem possible...
and b) the usage needs a type ascription, which makes it much less useable, for example:
getAll[(String, String)]((f1, f2))(to, TimeUnit.SECONDS) // doesn't compile without [(String, String)]
Scastie here

In match types case Tuple.Map[fh *: ft, Future] can make sense. But in pattern matching case fs: Tuple.Map[fh *: ft, Future] is just case fs: Tuple.Map[_, _] because of type erasure.
Currently on value level match types work not so well (many things can't be inferred). Good old type classes can be better.
I guess you meant Await.result instead of not existing Future.get.
Try to make the method inline and add implicit hints summonFrom { _: some_evidence => ... } where needed
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)
}
}
}
}
Testing:
getAll[(Int, String)](Future(1), Future("a"))(5000, MILLISECONDS) // (1,a)
Maybe it's better to define getAll with Tuple.InverseMap (without Tuple.Map at all)
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)
}
}
Testing:
getAll(Future(1), Future("a"))(5000, MILLISECONDS) // (1,a)
Now you don't need to specify the type parameter of getAll at the call site.
Easier would be to define getAll recursively both on type level (math types) and value level (pattern matching). Then you don't need implicit hints
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)
Please notice that if you replace the recursive definition of GetAll with just
type GetAll[T <: Tuple] = Tuple.InverseMap[T, Future]
the code stops to compile. You'll have to add implicit hints again.
I'm reminding you the rules of match types:
This special mode of typing for match expressions is only used when the following conditions are met:
The match expression patterns do not have guards
The match expression scrutinee's type is a subtype of the match type scrutinee's type
The match expression and the match type have the same number of cases
The match expression patterns are all Typed Patterns, and these types are =:= to their corresponding type patterns in the match
type
The compiler seems not to recognize the match-type definition accompanying a pattern-match definition if we specialize a type parameter along with introducing a type alias:
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
compiles and
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
does and
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
does but
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
doesn't (Scala 3.2.2). Also the order of cases is significant:
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
doesn't compile.
So the easiest implementation is
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
That's the order of cases as in the definition of Tuple.InverseMap https://github.com/lampepfl/dotty/blob/3.2.2/library/src/scala/Tuple.scala#L184-L187
See also
Scala 3: typed tuple zipping
Express function of arbitrary arity in vanilla Scala 3

Related

Getting MirroredElemLabels from Mirror

Scala 3's scala.deriving.Mirror has a type member MirroredElemLabels which is a tuple of string literals. What's the standard way to get that type as a value?
EDIT: here's the code that produces a compiler error from trying to use summonAll
case class Test(a: Int, b: String)
val mirror = implicitly[Mirror.ProductOf[Test]]
val labels = summonAll[mirror.MirroredElemLabels]
println(labels)
cannot reduce inline match with
scrutinee: compiletime.erasedValue[App.mirror.MirroredElemLabels] : App.mirror.MirroredElemLabels
patterns : case _:EmptyTuple
case _:*:[t # _, ts # _]
Try to use scala.ValueOf
case class A(i: Int, s: String)
import scala.deriving.Mirror
import scala.compiletime.summonAll
val mirror = summon[Mirror.Of[A]]
type ValueOfs = Tuple.Map[mirror.MirroredElemLabels, ValueOf]
val valueOfs = summonAll[ValueOfs]
def values(t: Tuple): Tuple = t match
case (h: ValueOf[_]) *: t1 => h.value *: values(t1)
case EmptyTuple => EmptyTuple
values(valueOfs) // (i,s)
We can now use scala.compiletime.constValueTuple
inline def constValueTuple[T <: Tuple]: T =
(inline erasedValue[T] match
case _: EmptyTuple => EmptyTuple
case _: (t *: ts) => constValue[t] *: constValueTuple[ts]
).asInstanceOf[T]
and the return type will be precise
case class A(i: Int, s: String)
val mirror = summon[Mirror.Of[A]]
val res = constValueTuple[mirror.MirroredElemLabels] // (i,s)
res: ("i", "s") // compiles
Tested in 3.2.0
https://docs.scala-lang.org/scala3/reference/contextual/derivation.html
https://docs.scala-lang.org/scala3/reference/metaprogramming/compiletime-ops.html

dotty / scala3 unmap a mapped tuple type to its constituent types [duplicate]

I am trying to create a function, which takes a tuple of higher-kinded types and applies a function to the types within the higher-kinded types.
In the example below, there is a trait Get[A] which is our higher-kinded type. There is also a tuple of Get's: (Get[String],Get[Int]) as well as function from (String,Int) => Person.
Scala-3 has a Match-Type called InverseMap which converts the type (Get[String], Get[Int]) into what is essentially the type (String,Int).
So the ultimate goal is to write a function which can take a tuple with any number of Get[_] types and a function whose input matches the InserveMap types and finally return a Get[_], where the wrapped type is the result of the function.
I have attempted to create a function called genericF below to show the desired behavior, though it may not be correct -- but I think it does at least show the proper intent.
case class Person(name: String, age: Int)
trait Get[A] {
def get: A
}
case class Put[A](get: A) extends Get[A]
val t: (Get[String], Get[Int]) = (Put("Bob"), Put(42))
val fPerson: (String,Int) => Person = Person.apply _
def genericF[T<:Tuple,I<:Tuple.InverseMap[T,Get],B](f: I => B, t: T): Get[B] = ???
val person: Get[Person] = genericF(fPerson, t)
I have set up a Scastie here: https://scastie.scala-lang.org/OleTraveler/QIyNHPLHQIKPv0lgsYbujA/23
Your code is almost compiling already - the only thing is that fPerson is of type (String, Int) => Person instead of ((String, Int)) => Person (taking a tuple instead of 2 separate parameters).
The solution below this one is not nice, although it is perhaps more efficient for TupleXXL's. Here's a nicer version with typeclasses (Scastie):
val fPerson: ((String, Int)) => Person = Person.apply _
opaque type Extract[GT <: Tuple, RT <: Tuple] = GT => RT
given Extract[EmptyTuple, EmptyTuple] = Predef.identity
given [A, PG <: Tuple, PR <: Tuple](using p: Extract[PG, PR])
as Extract[Get[A] *: PG, A *: PR] = {
case h *: t => h.get *: p(t)
}
def genericF[GT <: Tuple, RT <: Tuple, B](
f: RT => B,
t: GT
)(using extract: Extract[GT, RT]): Get[B] = Put(f(extract(t)))
Here's one way you could implement genericF using Tuple.InverseMap (note that I switched the two parameters to genericF:
val fPerson: ((String, Int)) => Person = Person.apply _
type ExtractG = [G] =>> G match {
case Get[a] => a
}
type AllGs[T <: Tuple] = T match {
case EmptyTuple => DummyImplicit
case Get[_] *: t => AllGs[t]
case _ => Nothing
}
def extract[T <: Tuple](t: T)(using AllGs[T]): Tuple.InverseMap[T, Get] =
t.map {
[G] => (g: G) => g.asInstanceOf[Get[_]].get.asInstanceOf[ExtractG[G]]
}.asInstanceOf[Tuple.InverseMap[T, Get]]
def genericF[B](
t: Tuple,
f: Tuple.InverseMap[t.type, Get] => B
)(using AllGs[t.type]): Get[B] = Put(f(extract(t)))
val person: Get[Person] = genericF(t, fPerson)
ExtractG is to make the PolyFunction compile, because it requires you apply a type constructor to its type parameter.
AllGs is to verify that the tuple consists only of Gets, because as pointed out by Dmytro Mitin, it isn't typesafe otherwise. If it's all Gets, the type becomes DummyImplicit, which Scala provides for us. Otherwise, it's Nothing. I guess it could conflict with other implicit/given Nothings in scope, but if you do have one already, you're screwed anyways.
Note that this will work only when you have Get and will need some modification if you also want it to work for tuples like (Put[String], GetSubclass[Int]).
Travis Stevens, the OP, has managed to get the solution above this one to work without creating AllGs, by using IsMappedBy. This is what they got (Scastie):
val fPerson: ((String, Int)) => Person = Person.apply _
type ExtractG = [G] =>> G match {
case Get[a] => a
}
def extract[T <: Tuple, I <: Tuple.InverseMap[T, Get]](
t: T
)(using Tuple.IsMappedBy[Get][T]): I =
t.map {
[G] => (g: G) => g.asInstanceOf[Get[_]].get.asInstanceOf[ExtractG[G]]
}.asInstanceOf[I]
def genericF[T <: Tuple, I <: Tuple.InverseMap[T, Get], B](
t: T,
f: I => B
)(using Tuple.IsMappedBy[Get][T]): Get[B] = Put(f(extract(t)))
And here's one using dependent types, just for fun (Scastie):
type Extract[T <: Tuple] <: Tuple = T match {
case EmptyTuple => EmptyTuple
case Get[a] *: t => a *: Extract[t]
}
type AllGs[T <: Tuple] = T match {
case EmptyTuple => DummyImplicit
case Get[_] *: t => AllGs[t]
case _ => Nothing
}
def genericF[T <: Tuple : AllGs, B](
t: T,
f: Extract[t.type] => B
): Get[B] = {
def extract[T <: Tuple](t: T): Extract[T] = t match {
case _: EmptyTuple => EmptyTuple
case (head *: tail): (Get[_] *: _) => head.get *: extract(tail)
}
Put(f(extract(t)))
}
I was hoping Extract wouldn't compile for tuples like (Put("foo"), 3), but unfortunately, AllGs is still necessary.

Pattern Matching/Checking Arity of Function1

Given the following method:
scala> def f: List[Any] => Any = xs => 1234 // this output does not matter
f: List[Any] => Any
Is it possible to pattern match on List[Any] => Any? I don't see an unapply method on Function1, so I believe the answer is no.
Here's what I'm trying to do:
Example:
def foo(x: Any) = x match {
case ... // to handle the case of List[Any] => Any]?
case ...
}
Perhaps I can figure out the arity of x: Any to differentiate between List[Any] => Any versus everything else (_)?
EDIT:
I hope that I do not have to rely on f.toString == <function1>.
No, you can't match exactly on List[Any] => Any due to type erasure, but you can match on Function1 itself:
def foo(x: Any): String = x match {
case _: Function1[_, _] => "some function1"
case _ => "other"
}
Any other matching, like case _: (List[Any] => Any) => "function from list to any" will act the same as case _: Function1[_, _] => "some function":
scala> def foo(x: Any): String = x match {
| case _: (List[Any] => Any) => "function from list to any"
| case _ => "other"
| }
<console>:8: warning: non-variable type argument List[Any] in type pattern List[Any] => Any is unchecked since it is eliminated by erasure
case _: (List[Any] => Any) => "function from list to any"
^
foo: (x: Any)String
scala> def aaa(l: Any): Any = null //it's `Any => Any` - not `List[Any] => Any`!!
aaa: (l: Any)Any
scala> foo(aaa _)
res11: String = function from list to any
scala> foo(1)
res12: String = other
You can match purely on type, even without an unapply method:
def foo(x: Any): String = x match {
case _: (Any => Any) => "some function1"
case _ => "other"
}
And then you can check:
foo(f) //"some function1"
foo(1) //"other"

Scala: Using a TypeTag to match on a Some's type

See the following code:
def createOption[T: TypeTag](referentialData: Any) : Option[T] = {
Option(referentialData) match {
case Some(camelMessage: CamelMessage) => {
Option(camelMessage.body) match {
case Some(option: T) => Some(option)
case _ => None
}
}
case _ => None
}
}
Basically I am looking to return an Option[T] if camelMessage.body is non-null and of type T.
The uses of Option(referentialData) is effectively referentialData != null
Likewise for Option(camelMessage.body)
How do I use the TypeTag to determine if camelMessage.body is of type T.
(I know this can be re-written to not use TypeTags and Options but I want to learn how to use TypeTags so please no suggestions to re-write, thanks!)
Edit
I tried a new approach as could not find a solution for the above, but could not get this one to work either:
def createOption[T](referentialData: Any) : Option[T] = {
Option(referentialData) match {
case Some(option) => Try(option.asInstanceOf[T]).toOption
case _ => None
}
}
When I invoke this using createOption[Long]("test") I was presuming to get a None back, but instead I got a Some(String)
Where am I going wrong here?
This is a duplicate of this one.
But you want to try it with ClassTag to show the limitation:
scala> def f[A: ClassTag](x: Any): Option[A] = x match {
| case y: A => println("OK"); Some(y) ; case _ => println("Nope"); None }
f: [A](x: Any)(implicit evidence$1: scala.reflect.ClassTag[A])Option[A]
scala> f[String]("foo")
OK
res0: Option[String] = Some(foo)
scala> f[Long](2L)
Nope
res1: Option[Long] = None
scala> f[java.lang.Long](new java.lang.Long(2L))
OK
res2: Option[Long] = Some(2)
scala> def f[A: TypeTag](x: Any): Option[A] = Option(x) match {
| case Some(y: A) => println("OK"); Some(y) ; case _ => println("Nope"); None }
<console>:51: warning: abstract type pattern A is unchecked since it is eliminated by erasure
case Some(y: A) => println("OK"); Some(y) ; case _ => println("Nope"); None }
^
f: [A](x: Any)(implicit evidence$1: reflect.runtime.universe.TypeTag[A])Option[A]

How to test a value on being AnyVal?

Tried this:
scala> 2.isInstanceOf[AnyVal]
<console>:8: error: type AnyVal cannot be used in a type pattern or isInstanceOf test
2.isInstanceOf[AnyVal]
^
and this:
scala> 12312 match {
| case _: AnyVal => true
| case _ => false
| }
<console>:9: error: type AnyVal cannot be used in a type pattern or isInstanceOf test
case _: AnyVal => true
^
The message is very informative. I get that I can't use it, but what should I do?
I assume you want to test if something is a primitive value:
def testAnyVal[T](x: T)(implicit evidence: T <:< AnyVal = null) = evidence != null
println(testAnyVal(1)) // true
println(testAnyVal("Hallo")) // false
println(testAnyVal(true)) // true
println(testAnyVal(Boolean.box(true))) // false
I assume that your type is actually Any or you'd already know whether it was AnyVal or not. Unfortunately, when your type is Any, you have to test all the primitive types separately (I have chosen the variable names here to match the internal JVM designations for the primitive types):
(2: Any) match {
case u: Unit => println("Unit")
case z: Boolean => println("Z")
case b: Byte => println("B")
case c: Char => println("C")
case s: Short => println("S")
case i: Int => println("I")
case j: Long => println("J")
case f: Float => println("F")
case d: Double => println("D")
case l: AnyRef => println("L")
}
This works, prints I, and does not give an incomplete match error.