I'm trying to create some simple custom String interpolator, and I'm successful as long as I don't try to use a type parameter.
import scala.concurrent.Future
object StringImplicits {
implicit class FailureStringContext (val sc : StringContext) extends AnyVal {
// This WORKS, but it's specific to Future :(
def fail[T](args : Any*): Future[T] = {
val orig = sc.s (args : _*)
Future.exception[T](new Exception(orig))
}
// I want this to work for Option,Try,Future!!
def fail[M,T](args:Any*): M[T] = {
val orig = sc.s (args : _*)
// Obviously does not work..
M match {
case Future => Future.exception(new Exception(orig))
case Option => None
case Try => Failure(new Exception(orig))
case _ => ???
}
}
}
}
Can I get this to work? I can't use parametric polymorphism because I'm not the one defining those three types.
What's the equivalent in the type level for that pseudo-code pattern match?
LATEST ATTEMPT
My latest attempt was to use implicitly, but I don't have such implicit! I'd be actually interested to grab a hold of the type that the compiler wants me to return according to type inference.
def fail[T, M[T]](args:Any*): M[T] = {
val orig = sc.s(args: _*)
implicitly[M[T]] match {
case _:Future[T] => Future.exception(new Exception(orig))
case _ => ???
}
}
<console>:18: error: could not find implicit value for parameter e: M[T]
implicitly[M[T]] match {
^
<console>:19: error: value exception is not a member of object scala.concurrent.Future
case _: Future[T] => Future.exception(new Exception(orig))
^
In my opinion the simplest is to rely on good old overloading: just define a different overload for each type that you want to handle.
Now of course, there is the problem of having different overloads with the same signature, and as usual in scala, you can use tricks to work around them. Here we'll add dummy implicit parameters to force each overload to have a distinct signature. Not pretty but it works and will suffice in this case.
import scala.concurrent.Future
import scala.util.{Try, Failure}
implicit class FailureStringContext (val sc : StringContext) extends AnyVal {
def fail[T](args : Any*): Future[T] = {
Future.failed[T](new Exception(sc.s (args : _*)))
}
def fail[T](args : Any*)(implicit dummy: DummyImplicit): Option[T] = {
Option.empty[T]
}
def fail[T](args : Any*)(implicit dummy: DummyImplicit, dummy2: DummyImplicit): Try[T] = {
Failure[T](new Exception(sc.s (args : _*)))
}
}
And tada:
scala> fail"oops": Option[String]
res6: Option[String] = None
scala> fail"oops": Future[String]
res7: scala.concurrent.Future[String] = scala.concurrent.impl.Promise$KeptPromise#6fc1a8f6
scala> fail"oops": Try[String]
res8: scala.util.Try[String] = Failure(java.lang.Exception: oops)
Related
Is there a clean way to access the default values of a case class fields when performing type class derivation in Scala 3 using Mirrors? For example:
case class Foo(s: String = "bar", i: Int, d: Double = Math.PI)
Mirror.Product.MirroredElemLabels will be set to ("s", "i", "d"). Is there anything like: (Some["bar"], None, Some[3.141592653589793])?
If not could this be achieved using Macros? Can I use the Mirrors and Macros simultaneously to derive a type class instance?
You'll have to write a macro working with methods named like <init>$default$1, <init>$default$2, ... in companion object
import scala.quoted.*
inline def printDefaults[T]: Unit = ${printDefaultsImpl[T]}
def printDefaultsImpl[T](using Quotes, Type[T]): Expr[Unit] = {
import quotes.reflect.*
(1 to 3).map(i =>
TypeRepr.of[T].typeSymbol
.companionClass
.declaredMethod(s"$$lessinit$$greater$$default$$$i")
.headOption
.flatMap(_.tree.asInstanceOf[DefDef].rhs)
).foreach(println)
'{()}
}
printDefaults[Foo]
//Some(Literal(Constant(bar)))
//None
//Some(Select(Ident(Math),PI))
Mirrors and macros can work together:
import scala.quoted.*
import scala.deriving.*
trait Default[T] {
type Out <: Tuple
def defaults: Out
}
object Default {
transparent inline given mkDefault[T](using
m: Mirror.ProductOf[T],
s: ValueOf[Tuple.Size[m.MirroredElemTypes]]
): Default[T] =
new Default[T] {
type Out = Tuple.Map[m.MirroredElemTypes, Option]
def defaults = getDefaults[T](s.value).asInstanceOf[Out]
}
inline def getDefaults[T](inline s: Int): Tuple = ${getDefaultsImpl[T]('s)}
def getDefaultsImpl[T](s: Expr[Int])(using Quotes, Type[T]): Expr[Tuple] = {
import quotes.reflect.*
val n = s.asTerm.underlying.asInstanceOf[Literal].constant.value.asInstanceOf[Int]
val terms: List[Option[Term]] =
(1 to n).toList.map(i =>
TypeRepr.of[T].typeSymbol
.companionClass
.declaredMethod(s"$$lessinit$$greater$$default$$$i")
.headOption
.flatMap(_.tree.asInstanceOf[DefDef].rhs)
)
def exprOfOption[T](oet: Option[Expr[T]])(using Type[T], Quotes): Expr[Option[T]] = oet match {
case None => Expr(None)
case Some(et) => '{Some($et)}
}
val exprs: List[Option[Expr[Any]]] = terms.map(_.map(_.asExprOf[Any]))
val exprs1: List[Expr[Option[Any]]] = exprs.map(exprOfOption)
Expr.ofTupleFromSeq(exprs1)
}
}
Usage:
val d = summon[Default[Foo]]
summon[d.Out =:= (Option[String], Option[Int], Option[Double])] // compiles
d.defaults // (Some(bar),None,Some(3.141592653589793))
As Dmytro suggests, information is carried in methods <init>default$x of the class companion object.
However, Quotes discourages accessing a symbol's tree in a macro:
https://github.com/lampepfl/dotty/blob/main/library/src/scala/quoted/Quotes.scala#L3628.
Symbol's tree is lost, unless program is compiled with -Yretain-trees)
It is better to let the macro evaluate <init>default$x, rather than copy the right hand side of its definition.
One can do so by expressing terms as :
val terms: List[Option[Term]] =
(1 to n).toList.map(i =>
TypeRepr.of[T].typeSymbol
.companionClass
.declaredMethod(s"$$lessinit$$greater$$default$$$i")
.headOption
.map(Select(Ref(TypeRepr.of[T].typeSymbol.companionModule),_))
)
Is there a clean way to access the default values of a case class fields when performing type class derivation in Scala 3 using Mirrors? For example:
case class Foo(s: String = "bar", i: Int, d: Double = Math.PI)
Mirror.Product.MirroredElemLabels will be set to ("s", "i", "d"). Is there anything like: (Some["bar"], None, Some[3.141592653589793])?
If not could this be achieved using Macros? Can I use the Mirrors and Macros simultaneously to derive a type class instance?
You'll have to write a macro working with methods named like <init>$default$1, <init>$default$2, ... in companion object
import scala.quoted.*
inline def printDefaults[T]: Unit = ${printDefaultsImpl[T]}
def printDefaultsImpl[T](using Quotes, Type[T]): Expr[Unit] = {
import quotes.reflect.*
(1 to 3).map(i =>
TypeRepr.of[T].typeSymbol
.companionClass
.declaredMethod(s"$$lessinit$$greater$$default$$$i")
.headOption
.flatMap(_.tree.asInstanceOf[DefDef].rhs)
).foreach(println)
'{()}
}
printDefaults[Foo]
//Some(Literal(Constant(bar)))
//None
//Some(Select(Ident(Math),PI))
Mirrors and macros can work together:
import scala.quoted.*
import scala.deriving.*
trait Default[T] {
type Out <: Tuple
def defaults: Out
}
object Default {
transparent inline given mkDefault[T](using
m: Mirror.ProductOf[T],
s: ValueOf[Tuple.Size[m.MirroredElemTypes]]
): Default[T] =
new Default[T] {
type Out = Tuple.Map[m.MirroredElemTypes, Option]
def defaults = getDefaults[T](s.value).asInstanceOf[Out]
}
inline def getDefaults[T](inline s: Int): Tuple = ${getDefaultsImpl[T]('s)}
def getDefaultsImpl[T](s: Expr[Int])(using Quotes, Type[T]): Expr[Tuple] = {
import quotes.reflect.*
val n = s.asTerm.underlying.asInstanceOf[Literal].constant.value.asInstanceOf[Int]
val terms: List[Option[Term]] =
(1 to n).toList.map(i =>
TypeRepr.of[T].typeSymbol
.companionClass
.declaredMethod(s"$$lessinit$$greater$$default$$$i")
.headOption
.flatMap(_.tree.asInstanceOf[DefDef].rhs)
)
def exprOfOption[T](oet: Option[Expr[T]])(using Type[T], Quotes): Expr[Option[T]] = oet match {
case None => Expr(None)
case Some(et) => '{Some($et)}
}
val exprs: List[Option[Expr[Any]]] = terms.map(_.map(_.asExprOf[Any]))
val exprs1: List[Expr[Option[Any]]] = exprs.map(exprOfOption)
Expr.ofTupleFromSeq(exprs1)
}
}
Usage:
val d = summon[Default[Foo]]
summon[d.Out =:= (Option[String], Option[Int], Option[Double])] // compiles
d.defaults // (Some(bar),None,Some(3.141592653589793))
As Dmytro suggests, information is carried in methods <init>default$x of the class companion object.
However, Quotes discourages accessing a symbol's tree in a macro:
https://github.com/lampepfl/dotty/blob/main/library/src/scala/quoted/Quotes.scala#L3628.
Symbol's tree is lost, unless program is compiled with -Yretain-trees)
It is better to let the macro evaluate <init>default$x, rather than copy the right hand side of its definition.
One can do so by expressing terms as :
val terms: List[Option[Term]] =
(1 to n).toList.map(i =>
TypeRepr.of[T].typeSymbol
.companionClass
.declaredMethod(s"$$lessinit$$greater$$default$$$i")
.headOption
.map(Select(Ref(TypeRepr.of[T].typeSymbol.companionModule),_))
)
Suppose I've got a few case classes, e.g.:
case class C(c1: Int, c2: Double, c3: Option[String])
case class B(b: Int, cs: Seq[C])
case class A(a: String, bs: Seq[B])
Now I would like to generate a few instances of A with random values for tests.
I am looking for a generic way to do that. I can probably do it with runtime reflection but I prefer a compile-time solution.
def randomInstance[A](a: A): A = ???
How can I do it ? Can it be done with shapeless ?
The easiest way for you to do that would be using ScalaCheck. You do so by defining a Gen[A] for your instances:
import org.scalacheck.Gen
final case class C(c1: Int, c2: Double, c3: Option[String])
object C {
val cGen: Gen[C] = for {
c1 <- Gen.posNum[Int]
c2 <- Gen.posNum[Double]
c3 <- Gen.option(Gen.oneOf("foo", "bar", "hello"))
} yield C(c1, c2, c3)
}
And you consume it:
object F {
def main(args: Array[String]): Unit = {
val randomC: C = C.cGen.sample.get
}
}
On top of that, you can add scalacheck-shapeless which generates the Gen[A] for you, with completely random values (where you have no control over them).
You may also want to look into random-data-generator (thanks #Gabriele Petronella), which simplifies things even further. From the docs:
import com.danielasfregola.randomdatagenerator.RandomDataGenerator
object MyApp extends RandomDataGenerator {
case class Example(text: String, n: Int)
val example: Example = random[Example]
// Example(ਈ䈦㈾钜㔪旅ꪔ墛炝푰⡨䌆ᵅ퍧咪, 73967257)
}
This is also especially helpful in property based testing.
We've just moved away from scalacheck-shapeless and use Scala/Java reflection instead.
The main reasons are (1) scalacheck-shapeless uses Macros (slow compilation), (2) the API is a bit more verbose than my liking, and (3) the generated values are way too wild (e.g. generating strings with Japanese characters).
However, setting it up is a bit more involved. Here is a full working code that you can copy into your codebase:
import scala.reflect.api
import scala.reflect.api.{TypeCreator, Universe}
import scala.reflect.runtime.universe._
object Maker {
val mirror = runtimeMirror(getClass.getClassLoader)
var makerRunNumber = 1
def apply[T: TypeTag]: T = {
val method = typeOf[T].companion.decl(TermName("apply")).asMethod
val params = method.paramLists.head
val args = params.map { param =>
makerRunNumber += 1
param.info match {
case t if t <:< typeOf[Enumeration#Value] => chooseEnumValue(convert(t).asInstanceOf[TypeTag[_ <: Enumeration]])
case t if t =:= typeOf[Int] => makerRunNumber
case t if t =:= typeOf[Long] => makerRunNumber
case t if t =:= typeOf[Date] => new Date(Time.now.inMillis)
case t if t <:< typeOf[Option[_]] => None
case t if t =:= typeOf[String] && param.name.decodedName.toString.toLowerCase.contains("email") => s"random-$arbitrary#give.asia"
case t if t =:= typeOf[String] => s"arbitrary-$makerRunNumber"
case t if t =:= typeOf[Boolean] => false
case t if t <:< typeOf[Seq[_]] => List.empty
case t if t <:< typeOf[Map[_, _]] => Map.empty
// Add more special cases here.
case t if isCaseClass(t) => apply(convert(t))
case t => throw new Exception(s"Maker doesn't support generating $t")
}
}
val obj = mirror.reflectModule(typeOf[T].typeSymbol.companion.asModule).instance
mirror.reflect(obj).reflectMethod(method)(args:_*).asInstanceOf[T]
}
def chooseEnumValue[E <: Enumeration: TypeTag]: E#Value = {
val parentType = typeOf[E].asInstanceOf[TypeRef].pre
val valuesMethod = parentType.baseType(typeOf[Enumeration].typeSymbol).decl(TermName("values")).asMethod
val obj = mirror.reflectModule(parentType.termSymbol.asModule).instance
mirror.reflect(obj).reflectMethod(valuesMethod)().asInstanceOf[E#ValueSet].head
}
def convert(tpe: Type): TypeTag[_] = {
TypeTag.apply(
runtimeMirror(getClass.getClassLoader),
new TypeCreator {
override def apply[U <: Universe with Singleton](m: api.Mirror[U]) = {
tpe.asInstanceOf[U # Type]
}
}
)
}
def isCaseClass(t: Type) = {
t.companion.decls.exists(_.name.decodedName.toString == "apply") &&
t.decls.exists(_.name.decodedName.toString == "copy")
}
}
And, when you want to use it, you can call:
val user = Maker[User]
val user2 = Maker[User].copy(email = "someemail#email.com")
The code above generates arbitrary and unique values. They aren't exactly randomised. It's best for using in tests.
Read our full blog post here: https://give.engineering/2018/08/24/instantiate-case-class-with-arbitrary-value.html
We've started using Magnolia, which provides a faster type class derivation compared to shapeless for derivation of Arbitrary instances.
Here is the library to use, and here is an example (docs):
case class Inner(int: Int, str: String)
case class Outer(inner: Inner)
// ScalaCheck Arbitrary
import magnolify.scalacheck.auto._
import org.scalacheck._ // implicit instances for Arbitrary[Int], etc.
val arb: Arbitrary[Outer] = implicitly[Arbitrary[Outer]]
arb.arbitrary.sample
// = Some(Outer(Inter(12345, abcde)))
I'm using the following Scala macro (heavily inspired by the code from this SO question) to get a list of all objects contained in a given package that inherit a specific trait:
object Macros {
def allObjects[T <: AnyRef](packageName: String): List[Any] = macro allObjectsImpl[T]
def allObjectsImpl[T <: AnyRef: c.WeakTypeTag](c: Context)(packageName: c.Expr[String]) = {
import c.universe._
val baseTraitSymbol = c.weakTypeOf[T].typeSymbol
val pkg = packageName.tree match {
case Literal(Constant(name: String)) => c.mirror.staticPackage(name)
}
val types = pkg.typeSignature.members.collect {
case moduleSymbol: ModuleSymbol if moduleSymbol.moduleClass.asClass.baseClasses contains baseTraitSymbol => Ident(moduleSymbol)
}.toList
val listApply = Select(reify(List).tree, TermName("apply"))
c.Expr[List[T]](Apply(listApply, types))
}
}
Which works fine.
I want to change the macro so that instead of getting all the objects in a package, it gets all the concrete classes, and provides a list containing an instance of each of them.
The AST when creating an instance of an Object looks like this:
scala> import scala.reflect.runtime.{universe => u}
import scala.reflect.runtime.{universe=>u}
scala> u showRaw ( u reify {new Object} )
res42: String = Expr(Apply(Select(New(Ident(java.lang.Object)), termNames.CONSTRUCTOR), List()))
So I thought changing my code to this would work:
object Macros {
def allInstances[T <: AnyRef](packageName: String): List[Any] = macro allInstancesImpl[T]
def allInstancesImpl[T <: AnyRef: c.WeakTypeTag](c: Context)(packageName: c.Expr[String]) = {
import c.universe._
val baseTraitSymbol = c.weakTypeOf[T].typeSymbol
val pkg = packageName.tree match {
case Literal(Constant(name: String)) => c.mirror.staticPackage(name)
}
def isConcreteChildClass(child: ClassSymbol, base: Symbol) = {
!child.isAbstract && (child.baseClasses contains base)
}
val types = pkg.typeSignature.members.collect {
case classSymbol: ClassSymbol if isConcreteChildClass(classSymbol, baseTraitSymbol) => {
Apply(Select(New(Ident(classSymbol.primaryConstructor)), termNames.CONSTRUCTOR), List())
}
}.toList
val listApply = Select(reify(List).tree, TermName("apply"))
c.Expr[List[T]](Apply(listApply, types))
}
}
However, when I try to use the updated macro code on a test package, I get the following error:
scala> Macros.allInstances[AnyRef]("test")
<console>:9: error: class type required but ()test.TestClass found
Macros.allInstances[AnyRef]("test")
From what I'm seeing, it looks like the macro is actually returning the constructor itself instead of returning the instance that should get built by the constructor, but I can't figure out what I'm missing.
The problem is in this line (reformatted for clarity):
Apply(
Select(New(Ident(classSymbol.primaryConstructor)), termNames.CONSTRUCTOR),
List()
)
You're essentially selecting the constructor twice. You could just drop primaryConstructor:
Apply(
Select(New(Ident(classSymbol)), termNames.CONSTRUCTOR),
List()
)
Using ApplyConstructor would also work:
ApplyConstructor(Ident(classSymbol), Nil)
Or you could just go with quasiquotes:
q"new ${Ident(classSymbol)}()"
The quasiquote solution is the most future-proof.
I am trying to cast a String to Int using extractors. My code looks as follows.
object Apply {
def unapply(s: String): Option[Int] = try {
Some(s.toInt)
} catch {
case _: java.lang.Exception => None
}
}
object App {
def toT[T](s: AnyRef): Option[T] = s match {
case v: T => Some(v)
case _ => None
}
def foo(param: String): Int = {
//reads a Map[String,String] m at runtime
toT[Int](m("offset")).getOrElse(0)
}
}
I get a runtime error: java.lang.String cannot be cast to java.lang.Integer. It seems the extractor is not being used at all. What should I do?
Edit: My use case is as follows. I am using play and I want to parse the query string passed in the url. I want to take the query string value (String) and use it as an Int, Double etc. For example,
val offset = getQueryStringAs[Int]("offset").getOrElse(0)
I think the biggest problem here is, that you seem to confuse casting and conversion. You have a Map[String, String] and therefore you can't cast the values to Int. You have to convert them. Luckily Scala adds the toInt method to strings through implicit conversion to StringOps.
This should work for you:
m("offset").toInt
Note that toInt will throw a java.lang.NumberFormatException if the string can not be converted to an integer.
edit:
What you want will afaik only work with typeclasses.
Here is an example:
trait StringConverter[A] {
def convert(x: String): A
}
implicit object StringToInt extends StringConverter[Int] {
def convert(x: String): Int = x.toInt
}
implicit object StringToDouble extends StringConverter[Double] {
def convert(x: String): Double = x.toDouble
}
implicit def string2StringConversion(x: String) = new {
def toT[A](implicit ev: StringConverter[A]) = ev.convert(x)
}
usage:
scala> "0.".toT[Double]
res6: Double = 0.0
There's a problem in your code, for which you should have received compiler warnings:
def toT[T](s: AnyRef): Option[T] = s match {
case v: T => Some(v) // this doesn't work, because T is erased
case _ => None
}
Now... where should Apply have been used? I see it declared, but I don't see it used anywhere.
EDIT
About the warning, look at the discussions around type erasure on Stack Overflow. For example, this answer I wrote on how to get around it -- though it's now deprecated with Scala 2.10.0.
To solve your problem I'd use type classes. For example:
abstract class Converter[T] {
def convert(s: String): T
}
object Converter {
def toConverter[T](converter: String => T): Converter[T] = new Converter[T] {
override def convert(s: String): T = converter(s)
}
implicit val intConverter = toConverter(_.toInt)
implicit val doubleConverter = toConverter(_.toDouble)
}
Then you can rewrite your method like this:
val map = Map("offset" -> "10", "price" -> "9.99")
def getQueryStringAs[T : Converter](key: String): Option[T] = {
val converter = implicitly[Converter[T]]
try {
Some(converter convert map(key))
} catch {
case ex: Exception => None
}
}
In use:
scala> getQueryStringAs[Int]("offset")
res1: Option[Int] = Some(10)
scala> getQueryStringAs[Double]("price")
res2: Option[Double] = Some(9.99)