Getting MirroredElemLabels from Mirror - scala

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

Related

scala 3 map tuple to futures of tuple types and back

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

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.

Getting around invariant result type in State

I would like to define a State that builds a concrete subtype of a trait, as per decodeFoo:
sealed trait Foo
case class Bar(s: String) extends Foo
case class Baz(i: Int) extends Foo
val int: State[Seq[Byte], Int] = State[Seq[Byte], Int] {
case bs if bs.length >= 4 =>
bs.drop(4) -> ByteBuffer.wrap(bs.take(4).toArray).getInt
case _ => sys.error(s"Insufficient data remains to parse int")
}
def bytes(len: Int): State[Seq[Byte], Seq[Byte]] = State[Seq[Byte], Seq[Byte]] {
case bs if bs.length >= len => bs.drop(len) -> bs.take(len)
case _ => sys.error(s"Insufficient data remains to parse $len bytes")
}
val bytes: State[Seq[Byte], Seq[Byte]] = for {
len <- int
bs <- bytes(len)
} yield bs
val string: State[Seq[Byte], String] = bytes.map(_.toArray).map(new String(_, Charset.forName("UTF-8")))
val decodeBar: State[Seq[Byte], Bar] = string.map(Bar)
val decodeBaz: State[Seq[Byte], Baz] = int.map(Baz)
val decodeFoo: State[Seq[Byte], Foo] = int.flatMap {
case 0 => decodeBar
case 1 => decodeBaz
}
This will not compile as State is defined in cats as type State[S, A] and the compiler responds:
Error:(36, 15) type mismatch;
found : cats.data.State[Seq[Byte],FooBarBaz.this.Bar]
(which expands to) cats.data.IndexedStateT[cats.Eval,Seq[Byte],Seq[Byte],FooBarBaz.this.Bar]
required: cats.data.IndexedStateT[cats.Eval,Seq[Byte],Seq[Byte],FooBarBaz.this.Foo]
Note: FooBarBaz.this.Bar <: FooBarBaz.this.Foo, but class IndexedStateT is invariant in type A.
You may wish to define A as +A instead. (SLS 4.5)
case 0 => decodeBar
I can work around this by widening the definitions of decodeBar & decodeBaz to be of type State[Seq[Byte], Foo]. Is that the best way forward? Or can I take a different approach that avoids widening these types?
Functor.widen
Functor.widen should do the trick. Full compilable example (with kind-projector):
import cats.data.State
import cats.Functor
object FunctorWidenExample {
locally {
sealed trait A
case class B() extends A
val s: State[Unit, B] = State.pure(new B())
val t: State[Unit, A] = Functor[State[Unit, ?]].widen[B, A](s)
}
}
in your case, it would be something like:
val decodeFoo: State[Seq[Byte], Foo] = int.flatMap {
case 0 => Functor[State[Seq[Byte], ?]].widen[Bar, Foo](decodeBar)
case 1 => Functor[State[Seq[Byte], ?]].widen[Bar, Foo](decodeBaz)
}
Other possible work-arounds
(not really necessary, just to demonstrate the syntax that might be less known):
Explicit type ascriptions:
val decodeFoo: State[Seq[Byte], Foo] = int.flatMap {
case 0 => decodeBar.map(x => (x: Foo))
case 1 => decodeBaz.map(x => (x: Foo))
}
Using <:< as method (those things actually do have a meaningful apply):
val decodeFoo: State[Seq[Byte], Foo] = int.flatMap {
case 0 => decodeBar.map(implicitly: Bar <:< Foo)
case 1 => decodeBaz.map(implicitly: Baz <:< Foo)
}

How to annotate a parameter as implicit in pattern match while extracting values

I have a class like
case class A(a: Int, b: String)
and a function
def f(a: Int)(implicit b: String) =???
Is it possible to do something like this?
val a = A(11, "hello")
a match {
case A(a, implicit b) => f(a)
}
How can I make the parameter b implicit without explicitly declaring it after extraction.
I wouldn't worry about passing the argument implicitly, since you can easily provide it explicitly in this particular case:
case class A(a: Int, b: String)
def f(a: Int)(implicit b: String) =???
val a = A(11, "hello")
a match {
case A(a, b) => f(a)(b)
}
If you must pass the value implicitly, it needs to be declared in scope. For example:
a match {
case A(a, b) => {
implicit val s = b
f(a)
}
}
Also, as has been pointed out, don't use implicit with a common type. It's better if you wrap it in another class:
case class A(a: Int, b: String)
case class SomeCustomString(s: String)
def f(a: Int)(implicit b: SomeCustomString) =???
val a = A(11, "hello")
a match {
case A(a, b) => {
implicit val s = SomeCustomString(b)
f(a)
}
}
If you could explain the use case for the implicit argument, I could provide a better example.
Update: There is a kind of way to do what you want:
case class SomeCustomString(s: String)
case class A(a: Int, b: String) {
implicit val s = SomeCustomString(b)
}
def f(a: Int)(implicit s: SomeCustomString) =???
val a = A(11, "hello")
import a._
f(a.a)
Or, if you must have it within a pattern match, that last bit would be:
a match {
case x: A => {
import x._
f(x.a)
}
}
Update 2: Or, as yet another approach (again, with implicit largely redundant):
case class SomeCustomString(s: String)
case class A(a: Int, b: String) {
implicit val s = SomeCustomString(b)
def invokeF = f(a)
}
def f(a: Int)(implicit s: SomeCustomString) =???
val a = A(11, "hello")
a.invokeF
or
a match {
case x: A => x.invokeF
}
Does that help?

Scala cast Any to Option[T]

Something like:
def cast[T](o: Any): Option[T] = o match {
case v: T => Some(v)
case _ => None
}
or:
def cast[T](c: Class[T], o: Any): Option[T] = o match {
case v: T => Some(v)
case _ => None
}
Is this a good idea? Is there a standard library equivalent?
Why do I get and how do I resolve the following Scala compiler warning:
Warning:(7, 13) abstract type pattern T is unchecked since it is eliminated by erasure
case v: T => Some(v)
Use class tag. Type information gets lost during runtime.So, you need provide type information which can be done using class tag.
import scala.reflect.ClassTag
def cast[T: ClassTag](o: Any): Option[T] = o match {
case v: T => Some(v)
case _ => None
}
Scala REPL
scala> cast[String]("hello")
res2: Option[String] = Some(hello)
scala> cast[Int]("scala")
res3: Option[Int] = None