I would like to print Scala source code of IF condition while being in THEN section.
Example: IF{ 2 + 2 < 5 } THEN { println("I am in THEN because: " + sourceCodeOfCondition) }
Let's skip THEN section right now, the question is: how to get source code of block after IF?
I assume that IF should be a macro...
Note: this question is redefined version of Macro to access source code of function at runtime where I described that { val i = 5; List(1, 2, 3); true }.logValueImpl works for me (according to other question Macro to access source code text at runtime).
Off-the-cuff implementation since I only have a minute:
import scala.reflect.macros.Context
import scala.language.experimental.macros
case class Conditional(conditionCode: String, value: Boolean) {
def THEN(doIt: Unit) = macro Conditional.THEN_impl
}
object Conditional {
def sourceCodeOfCondition: String = ???
def IF(condition: Boolean) = macro IF_impl
def IF_impl(c: Context)(condition: c.Expr[Boolean]): c.Expr[Conditional] = {
import c.universe._
c.Expr(q"Conditional(${ show(condition.tree) }, $condition)")
}
def THEN_impl(c: Context)(doIt: c.Expr[Unit]): c.Expr[Unit] = {
import c.universe._
val rewriter = new Transformer {
override def transform(tree: Tree) = tree match {
case Select(_, TermName("sourceCodeOfCondition")) =>
c.typeCheck(q"${ c.prefix.tree }.conditionCode")
case other => super.transform(other)
}
}
c.Expr(q"if (${ c.prefix.tree }.value) ${ rewriter.transform(doIt.tree) }")
}
}
And then:
object Demo {
import Conditional._
val x = 1
def demo = IF { x + 5 < 10 } THEN { println(sourceCodeOfCondition) }
}
And finally:
scala> Demo.demo
Demo.this.x.+(5).<(10)
It's a desugared representation of the source, but off the top of my head I think that's the best you're going to get.
See my blog post here for some discussion of the technique.
As of 2.13, you can also do this by wrapping the expression, which means you don't have to define a custom if function:
implicit def debugIf[A]: DebugIf => Unit = { cond: DebugIf =>
logger.info(s"condition = {}, result = ${cond.result}", cond.code)
}
decorateIfs {
if (System.currentTimeMillis() % 2 == 0) {
println("decorateIfs: if block")
} else {
println("decorateIfs: else block")
}
}
with the macro implementation:
def decorateIfs[A: c.WeakTypeTag](a: c.Expr[A])(output: c.Expr[DebugIf => Unit]): c.Expr[A] = {
def isEmpty(tree: Trees#Tree): Boolean = {
tree match {
case Literal(Constant(())) =>
true
case other =>
false
}
}
c.Expr[A] {
a.tree match {
// https://docs.scala-lang.org/overviews/quasiquotes/expression-details.html#if
case q"if ($cond) $thenp else $elsep" =>
val condSource = extractRange(cond) getOrElse ""
val printThen = q"$output(DebugIf($condSource, true))"
val elseThen = q"$output(DebugIf($condSource, false))"
val thenTree = q"""{ $printThen; $thenp }"""
val elseTree = if (isEmpty(elsep)) elsep else q"""{ $elseThen; $elsep }"""
q"if ($cond) $thenTree else $elseTree"
case other =>
other
}
}
}
private def extractRange(t: Trees#Tree): Option[String] = {
val pos = t.pos
val source = pos.source.content
if (pos.isRange) Option(new String(source.drop(pos.start).take(pos.end - pos.start))) else None
}
case class DebugIf(code: String, result: Boolean)
Related
I am not able to return Future[List[DiagnosisCode]] from fetchDiagnosisForUniqueCodes
import scala.concurrent._
import ExecutionContext.Implicits.global
case class DiagnosisCode(rootCode: String, uniqueCode: String, description: Option[String] = None)
object Database {
private val data: List[DiagnosisCode] = List(
DiagnosisCode("A00", "A001", Some("Cholera due to Vibrio cholerae")),
DiagnosisCode("A00", "A009", Some("Cholera, unspecified")),
DiagnosisCode("A08", "A080", Some("Rotaviral enteritis")),
DiagnosisCode("A08", "A083", Some("Other viral enteritis"))
)
def getAllUniqueCodes: Future[List[String]] = Future {
Database.data.map(_.uniqueCode)
}
def fetchDiagnosisForUniqueCode(uniqueCode: String): Future[Option[DiagnosisCode]] = Future {
Database.data.find(_.uniqueCode.equalsIgnoreCase(uniqueCode))
}
}
getAllUniqueCodes returns all unique codes from data List.
fetchDiagnosisForUniqueCode returns DiagnosisCode when uniqueCode matches.
From fetchDiagnosisForUniqueCodes, I would like to return Future[List[DiagnosisCode]] using getAllUniqueCodes() and fetchDiagnosisForUniqueCode(uniqueCode).*
def fetchDiagnosisForUniqueCodes: Future[List[DiagnosisCode]] = {
val xa: Future[List[Future[DiagnosisCode]]] = Database.getAllUniqueCodes.map { (xs:
List[String]) =>
xs.map { (uq: String) =>
Database.fetchDiagnosisForUniqueCode(uq)
}
}.map(n =>
n.map(y=>
y.map(_.head))) // Future[List[Future[DiagnosisCode]]]
}
If I understood your post correctly, your question is: "How can I convert a Future[List[Future[DiagnosisCode]]] into a Future[List[DiagnosisCode]]?"
The answer to that question would be: use Future.sequence:
// assuming an implicit ExecutionContext is in scope:
val xa: Future[List[Future[DiagnosisCode]]] = // ... your code here
val flattened: Future[List[DiagnosisCode]] =
xa.flatMap { listOfFutures =>
Future.sequence(listOfFutures)
}
I have one main class like this:
class Test {
def exe(first:String, second:String, task:String):String = {
task match {
case "A" => {
val obj = new A(first)
obj.defineSecond(second)
}
case "B" => {
val obj = new B(first)
obj.defineSecond(second)
}
case "C" => {
val obj = new C(first)
obj.defineSecond(second)
}
....so many cases
}
}
}
Instead of writing case in my Test class everytime a new class is added, I tried using the concept of reflection in scala.
Below is what I trying:
val m = ru.runtimeMirror(getClass.getClassLoader)
val classTest = ru.typeOf[Test].typeSymbol.asClass
val cm = m.reflectClass(classTest)
But getting error as "class Test is inner class, use reflectClass on an InstaneMirror to obtain its classMirror".
Can anyone knows how can I can avoid adding cases to my main class everytime a new class is created, instead I can write my main class in a way it will work for every case.
I guess you haven't provided all necessary information in your question. It's written that "class Test is inner class" in your error message but Test is not inner in your code snippet. If you want your runtime-reflection code to be fixed please provide code snippet that reflects actual use case.
Meanwhile you can try a macro (working at compile time)
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
class Test {
def exe(first: String, second: String, task: String): String = macro Test.exeImpl
}
object Test {
def exeImpl(c: blackbox.Context)(first: c.Tree, second: c.Tree, task: c.Tree): c.Tree = {
import c.universe._
val cases = Seq("A", "B", "C").map(name =>
cq"""${Literal(Constant(name))} => {
val obj = new ${TypeName(name)}($first)
obj.defineSecond($second)
}"""
)
q"$task match { case ..$cases }"
}
}
Usage:
class A(s: String) {
def defineSecond(s1: String): String = ""
}
class B(s: String) {
def defineSecond(s1: String): String = ""
}
class C(s: String) {
def defineSecond(s1: String): String = ""
}
new Test().exe("first", "second", "task")
//scalac: "task" match {
// case "A" => {
// val obj = new A("first");
// obj.defineSecond("second")
// }
// case "B" => {
// val obj = new B("first");
// obj.defineSecond("second")
// }
// case "C" => {
// val obj = new C("first");
// obj.defineSecond("second")
// }
//}
Cannot compile even if a function that handles function arguments is passed to macro.
A sample is shown below.
trait Generated[Z] {
def deserialize[A](t: A): Z
}
def from[A, Z](apl: A => Z): Generated[Z] = macro GeneratorMacro.from[A, Z]
class GeneratorMacro(val c: blackbox.Context) {
import c.universe._
def from[A: c.WeakTypeTag, Z: c.WeakTypeTag](apl: c.Expr[A => Z]): c.Expr[Generated[Z]] = {
reify {
new Generated[Z] {
def deserialize[A](t: A): Z = {
apl.splice.apply(t)
}
}
}
}
}
object Generation {
def apply(input: String): Generated[Int] = {
Generator.from[Option[String], Int] {
case Some(i) => input.toInt + i.toInt // compilation failed
case None => 0
}
}
}
An error occurs at this time.
Error: scalac: Error while emitting Generation.scala
value input
Isn't the class recompiled with the macro expanded?
If recompiled with inline expansion, no compilation error should occur.
object Generation {
def apply(input: String): Generated[Int] = {
new Generated[Int] {
def deserialize(t: String): Int = {
{
case Some(i) => input.toInt + i.toInt // input should be visible
case None => 0
}.apply(t)
}
}
}
}
What is going on and how to avoid it.
This seems to be impossible, AFAICS. The compiler creates an anonymous class for your code, but it will not capture the lexical context of the macro call inside it.
The code looks a bit like this after the lambdalift phase:
def apply(input: String): Generated[Int] = ({
new <$anon: Generated>()
}: Generated);
...
final class $anon extends Object with Generated {
def deserialize(t: Option): Int = {
... // <- your code is here !!
};
}
Of course, the code has no access to the input variable at this place.
This might be a bug in the Scala compiler...
The first error I have is
Warning:scalac: {
final class $anon extends App.this.Generated[Int] {
def <init>() = {
super.<init>();
()
};
def deserialize(t: Option[String]): Int = ((x0$1: Option[String]) => x0$1 match {
case (value: String)Some[String]((i # _)) => scala.Predef.augmentString(input).toInt.+(scala.Predef.augmentString(i).toInt)
case scala.None => 0
}).apply(t)
};
new $anon()
}
Error:(6, 43) object creation impossible. Missing implementation for:
def deserialize[A](t: A): Int // inherited from trait Generated
Generator.from[Option[String], Int] {
Obviously this is because you define
reify {
new Generated[Z] {
def deserialize(t: A): Z = {
...
instead of def deserialize[A](t: A): Z (#OlegPyzhcov pointed this out in the comments).
Regarding your error Error while emitting ... the thing is that
{
case Some(i: String) => input.toInt + i.toInt
case None => 0
}
has type not ... => ... i.e. Function1[..., ...] but actually PartialFunction[..., ...].
Try either
object Generator {
def from[Z](apl: PartialFunction[Any, Z]): Generated[Z] = macro GeneratorMacro.from[Z]
}
class GeneratorMacro(val c: blackbox.Context) {
import c.universe._
def from[Z: c.WeakTypeTag](apl: c.Expr[Any => Z]): c.Expr[Generated[Z]] = {
reify {
new Generated[Z] {
def deserialize[A](t: A): Z = {
apl.splice.apply(t)
}
}
}
}
}
Generator.from[Int]({
case Some(i: String) => input.toInt + i.toInt
case None => 0
})
or
object Generator {
def from[Z](apl: Any => Z): Generated[Z] = macro GeneratorMacro.from[Z]
}
class GeneratorMacro(val c: blackbox.Context) {
import c.universe._
def from[Z: c.WeakTypeTag](apl: c.Expr[Any => Z]): c.Expr[Generated[Z]] = {
reify {
new Generated[Z] {
def deserialize[A](t: A): Z = {
apl.splice.apply(t)
}
}
}
}
}
Generator.from[Int](new (Any => Int) {
override def apply(x: Any): Int = x match {
case Some(i: String) => input.toInt + i.toInt
case None => 0
}
})
Inspired by travisbrown, I'm trying to use a macro to create some "smart constructors".
Given
package mypkg
sealed trait Hello[A]
case class Ohayo[A,B](a: (A,B)) extends Hello[A]
and
val smartConstructors = FreeMacros.liftConstructors[Hello]
The macro should find all the subclasses of Hello, look at their constructors, and extract a few elements to populate this tree for the "smart constructor":
q"""
def $methodName[..$typeParams](...$paramLists): $baseType =
$companionSymbol[..$typeArgs](...$argLists)
"""
I hoped to get:
val smartConstructors = new {
def ohayo[A, B](a: (A, B)): Hello[A] = Ohayo[A, B](a)
}
but instead get:
error: type mismatch;
found : (A(in class Ohayo), B(in class Ohayo))
required: ((some other)A(in class Ohayo), (some other)B(in class Ohayo))
val liftedConstructors = FreeMacros.liftConstructors[Hello]
At a glance, the tree looks ok to me:
scala> q" new { ..$wellTyped }"
res1: u.Tree =
{
final class $anon extends scala.AnyRef {
def <init>() = {
super.<init>();
()
};
def ohayo[A, B](a: (A, B)): net.arya.constructors.Hello[A] = Ohayo[A, B](a)
};
new $anon()
}
but I guess it invisibly isn't. If I naively try to freshen up the typeParams with info.typeParams.map(p => TypeName(p.name.toString)), I get "can't splice A as type parameter" when I do the quasiquoting.
Where am I going wrong? Thanks for taking a look.
-Arya
import scala.language.experimental.macros
import scala.reflect.api.Universe
import scala.reflect.macros.whitebox
class FreeMacros(val c: whitebox.Context) {
import c.universe._
import FreeMacros._
def liftedImpl[F[_]](implicit t: c.WeakTypeTag[F[_]]): Tree = {
val atc = t.tpe
val childSymbols: Set[ClassSymbol] = subCaseClassSymbols(c.universe)(atc.typeSymbol.asClass)
val wellTyped = childSymbols.map(ctorsForSymbol(c.universe)(atc)).unzip
q"new { ..${wellTyped} }"
}
}
object FreeMacros {
def liftConstructors[F[_]]: Any = macro FreeMacros.liftedImpl[F]
def smartName(name: String): String = (
name.toList match {
case h :: t => h.toLower :: t
case Nil => Nil
}
).mkString
def subCaseClassSymbols(u: Universe)(root: u.ClassSymbol): Set[u.ClassSymbol] = {
val subclasses = root.knownDirectSubclasses
val cast = subclasses.map(_.asInstanceOf[u.ClassSymbol])
val partitioned = mapped.partition(_.isCaseClass)
partitioned match {
case (caseClasses, regularClasses) => caseClasses ++ regularClasses.flatMap(r => subCaseClassSymbols(u)(r))
}
}
def ctorsForSymbol(u: Universe)(atc: u.Type)(caseClass: u.ClassSymbol): (u.DefDef, u.DefDef) = {
import u._
import internal._
// these didn't help
// def clearTypeSymbol(s: Symbol): TypeSymbol = internal.newTypeSymbol(NoSymbol, s.name.toTypeName, s.pos, if(s.isImplicit)Flag.IMPLICIT else NoFlags)
// def clearTypeSymbol2(s: Symbol): TypeSymbol = internal.newTypeSymbol(NoSymbol, s.name.toTypeName, NoPosition, if(s.isImplicit)Flag.IMPLICIT else NoFlags)
// def clearTypeDef(d: TypeDef): TypeDef = internal.typeDef(clearTypeSymbol(d.symbol))
val companionSymbol: Symbol = caseClass.companion
val info: Type = caseClass.info
val primaryCtor: Symbol = caseClass.primaryConstructor
val method = primaryCtor.asMethod
val typeParams = info.typeParams.map(internal.typeDef(_))
// val typeParams = info.typeParams.map(s => typeDef(newTypeSymbol(NoSymbol, s.name.toTypeName, NoPosition, NoFlags)))
// val typeParams = info.typeParams.map(s => internal.typeDef(clearTypeSymbol2(s)))
val typeArgs = info.typeParams.map(_.name)
val paramLists = method.paramLists.map(_.map(internal.valDef(_)))
val argLists = method.paramLists.map(_.map(_.asTerm.name))
val baseType = info.baseType(atc.typeSymbol)
val List(returnType) = baseType.typeArgs
val methodName = TermName(smartName(caseClass.name.toString))
val wellTyped =
q"""
def $methodName[..$typeParams](...$paramLists): $baseType =
$companionSymbol[..$typeArgs](...$argLists)
"""
wellTyped
}
}
P.S. I have been experimenting with toolbox.untypecheck / typecheck per this article but haven't found a working combination.
you need using
clas.typeArgs.map(_.toString).map(name => {
TypeDef(Modifiers(Flag.PARAM),TypeName(name), List(),TypeBoundsTree(EmptyTree, EmptyTree))
}
replace
info.typeParams.map(p => TypeName(p.name.toString))
it si my code
object GetSealedSubClass {
def ol3[T]: Any = macro GetSealedSubClassImpl.ol3[T]
}
class GetSealedSubClassImpl(val c: Context) {
import c.universe._
def showInfo(s: String) =
c.info(c.enclosingPosition, s.split("\n").mkString("\n |---macro info---\n |", "\n |", ""), true)
def ol3[T: c.WeakTypeTag]: c.universe.Tree = {
//get all sub class
val subClass = c.weakTypeOf[T]
.typeSymbol.asClass.knownDirectSubclasses
.map(e => e.asClass.toType)
//check type params must ia s sealed class
if (subClass.size < 1)
c.abort(c.enclosingPosition, s"${c.weakTypeOf[T]} is not a sealed class")
// get sub class constructor params
val subConstructorParams = subClass.map { e =>
//get constructor
e.members.filter(_.isConstructor)
//if the class has many Constructor then you need filter the main Constructor
.head.map(s => s.asMethod)
//get function param list
}.map(_.asMethod.paramLists.head)
.map(_.map(e => q"""${e.name.toTermName}:${e.info} """))
val outfunc = subClass zip subConstructorParams map {
case (clas, parm) =>
q"def smartConstructors[..${
clas.typeArgs.map(_.toString).map(name => {
TypeDef(Modifiers(Flag.PARAM), TypeName(name), List(), TypeBoundsTree(EmptyTree, EmptyTree))
})
}](..${parm})=${clas.typeSymbol.name.toTermName} (..${parm})"
}
val outClass =
q"""
object Term{
..${outfunc}
}
"""
showInfo(show(outClass))
q"""{
$outClass
Term
}
"""
}
}
using like this
sealed trait Hello[A]
case class Ohayo[A, B](a: (A, B)) extends Hello[A]
object GetSealed extends App {
val a = GetSealedSubClass.ol3[Hello[_]]
val b=a.asInstanceOf[ {def smartConstructors[A, B](a: (A, B)): Ohayo[A, B]}].smartConstructors(1, 2).a
println(b)
}
With Scala's pattern matching I would like to confirm not only that two Strings are equal but for example, whether a String starts with, ends, or is contained in another etc.
I experimented with case classes and extractor objects, neither giving me a concise solution. So the solution I came up with looks like the following:
class StrMatches(private val str: Option[String]) {
def ^(prefix: String) = str.exists(_.startsWith(prefix))
def §(suffix: String) = str.exists(_.endsWith(suffix))
def %(infix: String) = str.exists(_.contains(infix))
def ~(approx: String) = str.exists(_.equalsIgnoreCase(approx))
def /(regex: scala.util.matching.Regex) = str.collect({ case regex() => true }).isDefined
def °(len: Int) = str.exists(_.length == len)
def °°(len: (Int, Int)) = str.exists(a => a.length >= len._1 && a.length <= len._2)
def `\\s*` = str.exists(_.trim.isEmpty)
override def toString = str.mkString
}
object StrMatches {
implicit def apply(x: Str) = new StrMatches(x)
def unapply(x: StrMatches) = x.str
implicit def unwrap(x: StrMatches) = x.toString
}
A client using the StrMatches class could look like the following:
object TestApp extends App {
val str = "foobar"
val strMatches = StrMatches(str)
if (strMatches ^ "foo") {
println(strMatches)
}
if (strMatches § "bar") {
println(strMatches)
}
if (strMatches % "ob") {
println(strMatches)
}
}
As opposed to writing:
object TestApp extends App {
val str: String = null // Just as an illustration for Scala interfacing Java.
if (str != null) {
if (str.startsWith("foo")) {
println(str)
}
if (strMatches.endsWith("bar")) {
println(str)
}
if (strMatches.contains("ob")) {
println(strMatches)
}
}
}
With what kind of solutions would you come up with?
You could use regular expressions. Then you could use pattern matching (which I think was the original intent of your question):
object TestApp extends App {
val str = "foobar"
val StartsWithFooRE = """^foo.*""".r
val EndsWithBarRE = """.*bar$""".r
val ContainsBoRE = """.*bo.*""".r
str match {
case StartsWithFooRE() => println(str)
case EndsWithBarRE() => println(str)
case ContainsBoRE() => println(str)
case _ =>
}
}
To make this more convenient, you could define an object with factory methods to construct the regular expressions. However, due to how pattern matching works, you'll still have to define the expressions outside of the match:
import scala.util.matching.Regex
object RegexFactory {
def startsWith(str: String) = new Regex("^%s.*" format str)
def endsWith(str: String) = new Regex(".*%s$" format str)
def contains(str: String) = new Regex(".*%s.*" format str)
}
object TestApp extends App {
val str = "foobar"
import RegexFactory._
val StartsWithFooRE = startsWith("foo")
val EndsWithBarRE = endsWith("bar")
val ContainsBoRE = contains("bo")
str match {
case StartsWithFooRE() => println(str)
case EndsWithBarRE() => println(str)
case ContainsBoRE() => println(str)
case _ =>
}
}