Using existing methods in macro - scala

Suppose i have some class with some methods
class Clz ... {
def someMethod: Map[String, Long] = ...
def id: Long = 0L
}
i'm need to reuse someMethod and overwrite id
i don't know why but it's throw Stackoverflow and also i'm need to do something with params/methods of Clz without returning the result
what i've tried:
object Macros {
def impl(c: whitebox.Context)(annottees: c.Tree*): c.Tree = {
import c.universe._
annottees match {
case (cls # q"$_ class $tpname[..$_] $_(...$paramss) extends { ..$_ } with ..$_ { $_ => ..$stats }") :: Nil =>
val someMethodM = stats
.filter(case q"$_ def $methodName: $_ = $_" => methodName.toString == "someMethod")
.map {
case q"$_ def $_: $_ = $res" => res
}
q"""
$cls
object ClzCompanion {
val someMethodsRef: Map[String, Int] = Map.apply(..$someMethodM)
..${
paramss
.flatten
.foreach {
case q"$_ val $nname: $tpt = $valuee" =>
// simply do something with valuee and nothing else
// "fire and forget"
}
}
..${
paramss
.flatten
.map {
case q"$_ val $nname: $tpt = $valuee" =>
// here logic, if this nname in someMethodsRef then
// someMethodsRef.find({ case (key, _) => key == nname) match {
// case Some(_) => "def $nname: ($tpt, Int) = ... // new method with body
// case None => do nothing...
}
}
}
"""
}
}
}
how i can overwrite the id method at Clz?
and why it throws StackOverflow??

Related

Scala pattern matching on None and Some() in the same case

What is the most elegant case to have None and Some() in the same case? Something like:
val data: Option[Int] = getSomeData()
data match {
case None || Some(data) && data > 50 =>
case _ =>
}
You can use Option.forall as condition.
def foo(data: Option[Int]): Unit =
if (data.forall(_ > 50)) println("OK")
else println("KO")
foo(None)
// => OK
foo(Some(1))
// => KO
foo(Some(51))
// OK
Normally such pattern matching can be written as follows
data match {
case None => doSomething()
case Some(data) if data > 50 => doSomething()
case _ => doOther()
}
If such combination (None || Some(data) && data > 50) happens often you can introduce custom extractor
object GreaterThan50OrEmpty {
def unapply(arg: Option[Int]): Boolean = arg match {
case None => true
case Some(data) if data > 50 => true
case _ => false
}
}
data match {
case GreaterThan50OrEmpty() => println("matches pattern")
case _ => println("default")
}
You can even call it as you want
object `None || Some(data) && data > 50` {
def unapply(arg: Option[Int]): Boolean = arg match {
case None => true
case Some(data) if data > 50 => true
case _ => false
}
}
data match {
case `None || Some(data) && data > 50`() => println("matches pattern")
case _ => println("default")
}
Slightly more general approach
class GreaterThanOrEmpty(dataBound: Int) {
def unapply(arg: Option[Int]): Boolean = arg match {
case None => true
case Some(data) if data > dataBound => true
case _ => false
}
}
val GreaterThan50OrEmpty = new GreaterThanOrEmpty(50)
data match {
case GreaterThan50OrEmpty() => println("matches pattern")
case _ => println("default")
}
You can even generate such unapply automatically (although I guess it's not worth it)
import scala.annotation.{StaticAnnotation, compileTimeOnly}
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
#compileTimeOnly("enable macro paradise")
class extractor[A] extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro ExtractorMacro.impl
}
object ExtractorMacro {
def impl(c: blackbox.Context)(annottees: c.Tree*): c.Tree = {
import c.universe._
val typA = c.prefix.tree match {
case q"new extractor[$a]" => a
}
annottees match {
case q"$mods object $tname extends { ..$earlydefns } with ..$parents { $self => ..$body }" :: Nil =>
val cases = tname.decoded.split('|').map(s => s"case $s => true").mkString("\n")
val casesTree = c.parse(
s"""arg match {
| $cases
| case _ => false
|}""".stripMargin)
q"""$mods object $tname extends { ..$earlydefns } with ..$parents { $self =>
..$body
def unapply(arg: $typA): Boolean = $casesTree
}"""
case _ => c.abort(c.enclosingPosition, "not object")
}
}
}
Usage:
#extractor[Option[Int]]
object `Some(x) if x < 25 | None | Some(data) if data > 50`
//Warning:scalac: object ... extends scala.AnyRef {
// ...
// def unapply(arg: Option[Int]): Boolean = arg match {
// case Some((x # _)) if x.$less(25) => true
// case None => true
// case Some((data # _)) if data.$greater(50) => true
// case _ => false
// }
//}
def test(arg: Any) = arg match {
case `Some(x) if x < 25 | None | Some(data) if data > 50`() =>
println("matches pattern")
case _ => println("default")
}
test(None) // matches pattern
test(Some(51)) // matches pattern
test(Some(24)) // matches pattern
test(Some(30)) // default

How to substitute objects in tree-like Scala case class instance with derived objects?

Suppose I have a set of case classes that represent constants, variables, and unary and binary operations on them, similar to one from "Case Classes and Pattern Matching" chapter in Programming in Scala:
abstract class Value {
def basicEvaluate(varArray: Array[Double]): Double
def evaluate(varArray: Array[Double]) = basicEvaluate(varArray)
}
case class Constant(d: Double) extends Value {
override def basicEvaluate(varArray: Array[Double]) = d
}
case class Variable(i: Int) extends Value {
override def basicEvaluate(varArray: Array[Double]) = varArray(i)
}
case class Add(v1: Value, v2: Value) extends Value {
override def basicEvaluate(varArray: Array[Double]) = v1.evaluate(varArray) + v2.evaluate(varArray)
}
...
Then, suppose I have some means to produce expression trees that reuse certain subexpressions many times, and I wish to be able to evaluate the expression efficiently, so that each distinct subexpression gets evaluated only once. For this reason, I introduce a trait
trait UsingCache extends Value {
var cached: Option[Double] = None
override def evaluate(varArray: Array[Double]) = {
if (cached == None) {
cached = Some(basicEvaluate(varArray))
}
cached.get
}
}
Then, I can do the following:
val expr = new Variable(0) with UsingCache
val expr2 = new Add(expr, expr) with UsingCache
expr2.evaluate(Array(5.0))
and it works.
My question is - how to implement a function def extend(value: Value): UsingCache which would recursively replace each Value in the tree with a corresponding .. with UsingCache object? I wish to keep this logic decoupled from the individual subclasses of Value (e.g., when I add a new operation, it shouldn't contain any code specific for caching). Is there some way to do this using implicit conversion? Or some ideas how to use Scala reflection (I'm using Scala 2.12)?
Try macro
def extend(value: Value): UsingCache = macro extendImpl
def extendImpl(c: blackbox.Context)(value: c.Tree): c.Tree = {
import c.universe._
def transformExprss(exprss: Seq[Seq[Tree]]): Seq[Seq[Tree]] =
exprss.map(_.map(expr => if (expr.tpe <:< typeOf[Value]) q"extend($expr)" else expr))
value match {
case q"$expr.$tname.apply(...$exprss)" =>
val exprss1 = transformExprss(exprss)
q"new $expr.${tname.toTypeName}(...$exprss1) with UsingCache"
case q"${tname: TermName}.apply(...$exprss)" =>
val exprss1 = transformExprss(exprss)
q"new ${tname.toTypeName}(...$exprss1) with UsingCache"
}
}
extend(Add(Constant(1.0), Variable(2)))
//Warning:scalac: performing macro expansion App.extend(App.Add.apply(App.Constant.apply(1.0), App.Variable.apply(2))) at ...
//Warning:scalac: {
// final class $anon extends App.Add(extend(App.Constant.apply(1.0)), extend(App.Variable.apply(2))) with UsingCache {
// def <init>() = {
// super.<init>();
// ()
// }
// };
// new $anon()
//}
//Warning:scalac: performing macro expansion App.extend(App.Constant.apply(1.0)) at ...
//Warning:scalac: {
// final class $anon extends App.Constant(1.0) with UsingCache {
// def <init>() = {
// super.<init>();
// ()
// }
// };
// new $anon()
//}
//Warning:scalac: performing macro expansion App.extend(App.Variable.apply(2)) at ...
//Warning:scalac: {
// final class $anon extends App.Variable(2) with UsingCache {
// def <init>() = {
// super.<init>();
// ()
// }
// };
// new $anon()
//}
Here is a solution that uses a stack to do a depth-first traversal. It is tail call optimized, so will not suffer from stack overflow. The OP also asked that old cached values be reused, so a map is used for memoization.
object CachedValueTest2 {
def main(args: Array[String]) = {
val expr1 = Add(Add(Constant(1), Add(Variable(1), Constant(1))), Add(Constant(2), Constant(2)))
println(extend(expr1))
val expr2 = Add(Add(Constant(1), Add(Add(Variable(2), Constant(1)), Constant(1))), Add(Constant(2), Add(Variable(1), Constant(2))))
println(extend(expr2))
}
def extend(value: Value): UsingCache = {
def replace(input: Value, stack: List[(Add, Option[UsingCache], Option[UsingCache])], map: Map[Value, UsingCache]): UsingCache = {
input match {
case in # Constant(d) =>
val (v, newMap) = map.get(in) match {
case Some(entry) => (entry, map)
case None =>
val entry = new Constant(d) with UsingCache
(entry, map + (in -> entry))
}
popStack(v, stack, newMap)
case in # Variable(i) =>
val (v, newMap) = map.get(in) match {
case Some(entry) => (entry, map)
case None =>
val entry = new Variable(i) with UsingCache
(entry, map + (in -> entry))
}
popStack(v, stack, newMap)
case in # Add(v1, v2) =>
map.get(in) match {
case Some(entry) => entry
case None => replace(v1, (in, None, None) :: stack, map)
}
}
}
def popStack(input: UsingCache, stack: List[(Add, Option[UsingCache], Option[UsingCache])], map: Map[Value, UsingCache]): UsingCache = {
stack match {
case head :: tail =>
head match {
case (add, None, None) =>
replace(add.v2, (add, Some(input), None) :: tail, map)
case (add, Some(v1), None) =>
val v = new Add(v1, input) with UsingCache
val newMap = map + (add -> v)
popStack(v, tail, newMap)
}
case Nil => input
}
}
replace(value, List(), Map())
}
abstract class Value {
def basicEvaluate(varArray: Array[Double]): Double
def evaluate(varArray: Array[Double]) = basicEvaluate(varArray)
}
case class Constant(d: Double) extends Value {
override def basicEvaluate(varArray: Array[Double]) = d
}
case class Variable(i: Int) extends Value {
override def basicEvaluate(varArray: Array[Double]) = varArray(i)
}
case class Add(v1: Value, v2: Value) extends Value {
override def basicEvaluate(varArray: Array[Double]) = v1.evaluate(varArray) + v2.evaluate(varArray)
}
trait UsingCache extends Value {
var caches : Map[Array[Double], Double] = Map()
override def evaluate(varArray: Array[Double]) = {
caches.get(varArray) match {
case Some(result) =>
result
case None =>
val result = basicEvaluate(varArray)
caches = caches + (varArray -> result)
result
}
}
}
}

How to get Left from a method that returns Future?

def myMethod(myType: String) :Future[Future[Either[List[MyError], MyClass]]] {
for {
first <- runWithSeq(firstSource)
}
yield {
runWithSeq(secondSource)
.map {s ->
val mine = MyClass(s.head, lars)
val errors = myType match {
case "all" => Something.someMethod(mine)
}
(s, errors)
}
.map { x =>
x._2.leftMap(xs => {
addInfo(x._1.head, xs.toList)
}).toEither
}
}
}
for {
myStuff <- myMethod("something")
} yield {
myStuff.collect {
case(Left(errors), rowNumber) =>
MyCaseClass(errors, None) //compilation error here
}
}
I get compilation error on MyCaseClass that expected: List[MyError], found: Any
The signature of MyCaseClass is:
case class MyCaseClass(myErrors: List[ValidationError])
How can I fix this such that I can correctly call MyCaseClass inside the yield?
Your code example doesn't make much sense, and doesn't compile, but if runWithSeq() returns a Future then you should be able to eliminate the double Future return type like so.
for {
_ <- runWithSeq(firstSource)
scnd <- runWithSeq(secondSource)
} yield { ...
Your example is pretty hard to paste and fix
Abstact example for this
Class C may be whatever you want
def test(testval: Int)(implicit ec: ExecutionContext): Future[Future[Either[String, Int]]] = {
Future(Future{
if (testval % 2 == 0) Right(testval) else Left("Smth wrong")
})
}
implicit class FutureEitherExt[A, B](ft: Future[Either[A, B]]) {
def EitherMatch[C](f1: A => C, f2: B => C)(implicit ec: ExecutionContext): Future[C] = {
ft.map {
case Left(value) => f1(value)
case Right(value) => f2(value)
}
}
}
val fl: Future[Either[String, Int]] = test(5).flatten
val result: Future[String] = fl.EitherMatch(identity, _.toString)

Scala slick comparing operator dynamically

I am developing a restful web service with play in scala and slick. I am trying to implement filtering. The only thing i cannot get working is to set the operator for the comparing operation dynamically. I want to support ===, =!=, <=, >= and LIKE.
I have a problem in the filteringOperator function with the generics but i cannot find it. The exception is as follows:
value === is not a member of HardwareRepo.this.dbConfig.driver.api.Rep[A]
Anybody an idea?
def filtering(exp: FilterExpression): (HardwareTable) => Rep[Option[Boolean]] = {
exp.field match {
case "serialNumber" => filteringOperator(exp, _.serialNumber, exp.value)
case "online" => filteringOperator(exp, _.online, Try(exp.value.toBoolean).getOrElse(false))
case _ => throw new FilterFieldNotSupportedException(s"Filter field '${exp.field}' not supported")
}
}
def filteringOperator[A](exp: FilterExpression, x: Rep[A], y: A): Rep[Option[Boolean]] = {
exp.operator match {
case FilterExpressionOperator.Equals => x === y
...
}
}
def all(offset: Int, size: Int, filter: List[FilterExpression]): Future[List[Hardware]] = {
var q = Hardwares.to[List]
//filtering
for (exp <- filter) {
try {
q = q.filter(filtering(exp))
} catch {
case ex: FilterFieldNotSupportedException => return Future.failed(ex)
}
}
//pagination
db.run(q.drop(offset).take(size).result)
}
whole sourcecode:
package models
import java.sql.Timestamp
import java.util.UUID
import javax.inject.Inject
import helper.{FilterExpression, FilterExpressionOperator, SortExpression, SortExpressionOrder}
import play.api.db.slick.DatabaseConfigProvider
import helper.exceptions.{EntityAlreadyExistsException, FilterFieldNotSupportedException, SortFieldNotSupportedException}
import slick.driver.JdbcProfile
import slick.lifted.ColumnOrdered
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.Try
case class Hardware(id: UUID,
createdAt: Timestamp,
serialNumber: String,
color: Int,
online: Boolean,
renterCount: Int)
class HardwareRepo #Inject()()(protected val dbConfigProvider: DatabaseConfigProvider) {
val dbConfig = dbConfigProvider.get[JdbcProfile]
val db = dbConfig.db
import dbConfig.driver.api._
private val Hardwares = TableQuery[HardwareTable]
val allowedSorting = Map( "id" -> { (hw: Hardware) => hw.id } )
private def _findById(id: UUID): DBIO[Option[Hardware]] =
Hardwares.filter(_.id === id).result.headOption
private def _findBySerialNumber(serialNumber: String): Query[HardwareTable, Hardware, List] =
Hardwares.filter(_.serialNumber === serialNumber).to[List]
def findById(id: UUID): Future[Option[Hardware]] =
db.run(_findById(id))
def findBySerialNumber(serialNumber: String): Future[List[Hardware]] =
db.run(_findBySerialNumber(serialNumber).result)
def sorting(exp: SortExpression): (HardwareTable) => ColumnOrdered[_] = {
exp.field match {
case "id" => if (exp.order == SortExpressionOrder.Asc) _.id.asc else _.id.desc
case "serialNumber" => if (exp.order == SortExpressionOrder.Asc) _.serialNumber.asc else _.serialNumber.desc
case "createdAt" => if (exp.order == SortExpressionOrder.Asc) _.createdAt.asc else _.createdAt.desc
case "color" => if (exp.order == SortExpressionOrder.Asc) _.color.asc else _.color.desc
case "online" => if (exp.order == SortExpressionOrder.Asc) _.online.asc else _.online.desc
case _ => throw new SortFieldNotSupportedException(s"Sort field '${exp.field}' not supported")
}
}
def filtering(exp: FilterExpression): (HardwareTable) => Rep[Option[Boolean]] = {
exp.field match {
case "serialNumber" => _.serialNumber === exp.value
case "online" => _.online === Try(exp.value.toBoolean).getOrElse(false)
case _ => throw new FilterFieldNotSupportedException(s"Filter field '${exp.field}' not supported")
}
}
def filteringOperator[A](exp: FilterExpression, x: Rep[A], y: A): Rep[Option[Boolean]] = {
exp.operator match {
case FilterExpressionOperator.Equals => x === y
}
}
def all(offset: Int, size: Int, sort: List[SortExpression], filter: List[FilterExpression]): Future[List[Hardware]] = {
var q = Hardwares.to[List]
//sorting
for (exp <- sort) {
try {
q = q.sortBy(sorting(exp))
} catch {
case ex: SortFieldNotSupportedException => return Future.failed(ex)
}
}
//filtering
for (exp <- filter) {
try {
q = q.filter(filtering(exp))
} catch {
case ex: FilterFieldNotSupportedException => return Future.failed(ex)
}
}
//pagination
db.run(q.drop(offset).take(size).result)
}
def exists(serialNumber: String): Future[Boolean] = {
db.run(Hardwares.filter(hw => hw.serialNumber === serialNumber).exists.result)
}
def create(serialNumber: Option[String]): Future[UUID] = {
if (serialNumber.isEmpty) {
db.run(Hardwares.map(hw => (hw.color)) returning Hardwares.map(_.id) += (0))
} else {
//check serial number
val action = (Hardwares.filter(hw => hw.serialNumber === serialNumber).exists.result.flatMap {
case true => DBIO.failed(new EntityAlreadyExistsException("Serialnumber already exists"))
case false => Hardwares.map(hw => (hw.color, hw.serialNumber)) returning Hardwares.map(_.id) += (0, serialNumber.get)
}).transactionally
db.run(action)
}
}
class HardwareTable(tag: Tag) extends Table[Hardware](tag, "hardware") {
def id = column[UUID]("id", O.PrimaryKey)
def createdAt = column[Timestamp]("created_at")
def serialNumber = column[String]("serial_number")
def color = column[Int]("color")
def online = column[Boolean]("online")
def renterCount = column[Int]("renter_count")
def * = (id, createdAt, serialNumber, color, online, renterCount) <> (Hardware.tupled, Hardware.unapply)
def ? = (id.?, createdAt.?, serialNumber.?, color.?, online.?, renterCount.?).shaped.<>({ r => import r._; _1.map(_ => Hardware.tupled((_1.get, _2.get, _3.get, _4.get, _5.get, _6.get))) }, (_: Any) => throw new Exception("Inserting into ? projection not supported."))
}
}
to resolve the error
value === is not a member of
HardwareRepo.this.dbConfig.driver.api.Rep[A]
for the === operator to work the profile api must be imported and available in scope. I would refactor the class HardwareRepo as below
class HardwareRepo #Inject() (protected val dbConfigProvider: DatabaseConfigProvider) {
with HasDatabaseConfigProvider[JdbcProfile] {
import driver.api._
/*
other repo methods
*/
}

Type conversion from Unit to Future[Boolean]

I have the following function and I would like to return Future[Boolean] but the IDE prompts that I return Unit. I am new in Scala. Can someone point me out what I am doing wrong?
def remove(loginInfo: LoginInfo): Future[Boolean] = {
val result = findObject(loginInfo)
result.onSuccess {
case Some(persistentPasswordInfo) =>
val removeResult = remove(persistentPasswordInfo._id.toString)
removeResult.map {
case Left(ex) => Future.successful(false)
case Right(b) => Future.successful(b)
}
case None => Future.successful(false)
}
}
Replace onSuccess with flatMap. Assuming your remove(x: String) method also returns a Future, that will also need to be flatMapped:
def remove(loginInfo: LoginInfo): Future[Boolean] = {
val result = findObject(loginInfo)
result.flatMap {
case Some(persistentPasswordInfo) =>
val removeResult = remove(persistentPasswordInfo._id.toString)
removeResult.flatMap {
case Left(ex) => Future.successful(false)
case Right(b) => Future.successful(b)
}
case None => Future.successful(false)
}
}