Given the following macro (thanks #TravisBrown for this help ):
JetDim.scala
case class JetDim(dimension: Int) {
require(dimension > 0)
}
object JetDim {
def validate(dimension: Int): Int = macro JetDimMacro.apply
def build(dimension: Int): JetDim = JetDim(validate(dimension))
}
JetDimMacro.scala
import reflect.macros.Context
object JetDimMacro {
sealed trait PosIntCheckResult
case class LteqZero(x: Int) extends PosIntCheckResult
case object NotConstant extends PosIntCheckResult
def apply(c: Context)(dimension: c.Expr[Int]): c.Expr[Int] = {
import c.universe._
getInt(c)(dimension) match {
case Right(_) => reify { dimension.splice }
case Left(LteqZero(x)) => c.abort(c.enclosingPosition, s"$x must be > 0.")
case Left(NotConstant) => reify { dimension.splice }
}
}
def getInt(c: Context)(dimension: c.Expr[Int]): Either[PosIntCheckResult, Int] = {
import c.universe._
dimension.tree match {
case Literal(Constant(x: Int)) => if (x > 0) Right(x) else Left(LteqZero(x))
case _ => Left(NotConstant)
}
}
}
It works from the REPL:
scala> import spire.math.JetDim
import spire.math.JetDim
scala> JetDim.validate(-55)
<console>:9: error: -55 must be > 0.
JetDim.validate(-55)
^
scala> JetDim.validate(100)
res1: Int = 100
But, I'd like to build this compile-time check (via the JetDimMacro) into the case class's apply method.
Attempt 1
case class JetDim(dimension: Int) {
require(dimension > 0)
}
object JetDim {
private def validate(dimension: Int): Int = macro JetDimMacro.apply
def build(dimension: Int): JetDim = JetDim(validate(dimension))
}
But that failed:
scala> import spire.math.JetDim
import spire.math.JetDim
scala> JetDim.build(-55)
java.lang.IllegalArgumentException: requirement failed
at scala.Predef$.require(Predef.scala:207)
at spire.math.JetDim.<init>(Jet.scala:21)
at spire.math.JetDim$.build(Jet.scala:26)
... 43 elided
Attempt 2
class JetDim(dim: Int) {
require(dim > 0)
def dimension: Int = dim
}
object JetDim {
private def validate(dimension: Int): Int = macro JetDimMacro.apply
def apply(dimension: Int): JetDim = {
validate(dimension)
new JetDim(dimension)
}
}
Yet that failed too:
scala> import spire.math.JetDim
import spire.math.JetDim
scala> JetDim(555)
res0: spire.math.JetDim = spire.math.JetDim#4b56f205
scala> JetDim(-555)
java.lang.IllegalArgumentException: requirement failed
at scala.Predef$.require(Predef.scala:207)
at spire.math.JetDim.<init>(Jet.scala:21)
at spire.math.JetDim$.apply(Jet.scala:30)
... 43 elided
I thought to modify JetDimMacro#apply to return a JetDim rather than an Int. However, JetDim lives in the core project, which, from what I see, depends on the macros project (where JetDimMacro lives).
How can I use this validate method from JetDim's companion object to check for positive int's at compile-time?
The problem is that by the time we call validate in apply we are no longer dealing with a constant (singleton type). So, validate gets a non-constant Int.
As an alternative, you could try using an implicit witness for positive ints, which JetDim then takes as a constructor. For instance, something like:
package com.example
case class JetDim(n: PositiveInt)
case class PositiveInt(value: Int) {
require(value > 0)
}
Then, we add an implicit (macro) conversion from Int => PositiveInt that does your check.
import scala.language.experimental.macros
import scala.reflect.macros.blackbox.Context
object PositiveInt {
implicit def wrapConstantInt(n: Int): PositiveInt = macro verifyPositiveInt
def verifyPositiveInt(c: Context)(n: c.Expr[Int]): c.Expr[PositiveInt] = {
import c.universe._
val tree = n.tree match {
case Literal(Constant(x: Int)) if x > 0 =>
q"_root_.com.example.PositiveInt($n)"
case Literal(Constant(x: Int)) =>
c.abort(c.enclosingPosition, s"$x <= 0")
case x =>
c.abort(c.enclosingPosition, s"cannot verify $x > 0")
}
c.Expr(tree)
}
}
You can then use JetDim(12), which will pass, or JetDim(-12), which will fail (the macro expands the Int to a PositiveInt).
Related
I have this class that manage reception of a stream of asynchronous messages. It has type argument, say A, and I like to implement a function that filter those messages by type, say B<:A
But my first implementation is not working due to type erasure (see example below). Is there a good way to so?
Here is a simplified example of my problem:
package test.filterByType
import scala.concurrent.Await
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
import scala.concurrent.Future
trait MessageStream[A]{ self =>
def next(): Future[A]
def filter[B<:A]: MessageStream[B] = new MessageStream[B]{
def next(): Future[B] = self.next.flatMap{
case b: B => Future.successful(b)
case _ => next()
}
}
}
case class MessageStreamImpl[A](msg: IndexedSeq[A]) extends MessageStream[A]{
private var index = 0
def next() = {
index += 1
Future.successful(msg(index-1))
}
}
object Main{
trait A
case class B(i: Int) extends A
case class C(i: Int) extends A
def main(args: Array[String]){
val msg: IndexedSeq[A] = (1 to 10).map{ i => if(i%2==0) B(i) else C(i) }
val streamOfA = MessageStreamImpl(msg)
val streamOfB = streamOfA.filter[B]
val b: B = Await.result(streamOfB.next(), 1 second)
}
}
At compilation, I get warning: abstract type pattern B is unchecked since it is eliminated by erasure, and indeed the code does not work. If I execute Main, I get the error:
lang.ClassCastException: test.filterByType.Main$C cannot be cast to test.filterByType.Main$B
Which happens because it does not filter out the first list item C(1)
This small adjustment does the trick
import scala.reflect.ClassTag
def filter[B<:A](implicit C: ClassTag[B]): MessageStream[B] = new MessageStream[B]{
def next(): Future[B] = self.next.flatMap{
case b: B => Future.successful(b)
case _ => next()
}
}
I've found an alternative that works:
def collect[B<:A](f: PartialFunction[A,B]) = new MessageStream[B] {
override def next(): Future[B] = self.next().flatMap { m =>
if (f.isDefinedAt(m)) Future.successful(f(m))
else next()
}
}
I have a function as follows:
def bar(x : Int) : Either[String, Future[Option[Foo]]] = {
Goo() recover { case e => Left("Some error string") }
}
As you can see, if the Future fails then it will hit the partial function inside the recover body. This will return Left and satisfies the left part of the Either type. What I am stuck on is how to return the Right if the Goo future completes successfully.
I tried the following:
def bar(x : Int) : Either[String, Future[Option[Foo]]] = {
Goo().map(x => Right(Future.successful(x))) recover { case e => Left("Some error string") }
}
However, I get a type error indicating that the return type for bar is Future[Either[String, Future[Foo]]].
How can I return a Right(x) where x is some value of type Foo?
UPDATE
def bar(x : Int) : Future[Either[String, Option[Foo]]] = {
Goo().map(x => Right(x)) recover { case e => Left("Some error string") }
}
You didn't define Goo, but i'll assume for the moment the following:
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
case class Foo(n: Int)
/** Takes a string and returns a future int-parsing of it */
def Goo(s: String): Future[Foo] = Future {
Foo(java.lang.Integer.parseInt(s)) // will throw an exception on non-int
}
Then if you want bar(s: String) to return Either[String, Option[Foo]] where the Option is a Some[Foo] if the number is parseable and positive, or None if parseable but non-positive and the String is an explanation of why it failed to parse, you could do:
import scala.concurrent.Await
import scala.concurrent.duration.Duration
import scala.util.control.NonFatal
def bar(s: String): Future[Either[String, Option[Foo]]] = {
Goo(s).map { foo: Foo =>
Right(if (foo.n > 0) Some(foo) else None)
}.recover {
case NonFatal(e) => Left("Failed to parse %s: %s".format(s, e))
}
}
voila:
scala> Await.result(bar("4"), Duration.Inf)
res1: Either[String,Option[Foo]] = Right(Some(Foo(4)))
scala> Await.result(bar("-4"), Duration.Inf)
res2: Either[String,Option[Foo]] = Right(None)
scala> Await.result(bar("four"), Duration.Inf)
res3: Either[String,Option[Foo]] = Left(Failed to parse four: java.lang.NumberFormatException: For input string: "four")
Given the following types:
sealed trait PosIntCheckResult
case class LteZero(x: Int) extends PosIntCheckResult
case object NotConstant extends PosIntCheckResult
I'm trying to write a macro that checks whether the given Int is greater than 0.
import reflect.macros.Context
def getInt(c: Context)(value: c.Expr[Int]): Either[PosIntCheckResult, Int] = {
import c.universe._
value.tree match {
case Literal(Constant(x)) => if (x > 0) Right(x) else Left(LteZero(x))
case _ => Left(NotConstant)
}
}
But Any shows up for the x value:
Test.scala:29: type mismatch;
found : Any
required: Int
case Literal(Constant(x)) =>
if (x > 0) Right(x) else Left(LteZero(x))
How can I get the compiler to expect an Int rather than Any?
You just have to match on patterns where x is an Int:
case Literal(Constant(x: Int)) => //When using x here, it is an Int
Read about pattern matching on type and the other types of pattern matching in the docs.
You should also note that your macro will need to return an Expr in order to work. You can use reify to construct the desired Exprs at each case. Read about reify and def macros here. I'm not sure why this needs to be a macro, but if you're just learning the techniques, something like this might work:
object Macros {
def getInt(value: Int): Either[PosIntCheckResult, Int] = macro getIntImpl
def getIntImpl(c: Context)(value: c.Expr[Int]): c.Expr[Either[PosIntCheckResult,Int]] = {
import c.universe._
value.tree match {
case x#Literal(Constant(const: Int)) if const > 0 => reify(Right(c.Expr[Int](x).splice))
case x#Literal(Constant(_: Int)) => reify(Left(LteZero(c.Expr[Int](x).splice)))
case _ => reify(Left(NotConstant))
}
}
}
def createFloatBuffer(data: Option[Quaternion]*): Option[FloatBuffer] = data match {
...
}
def createFloatBuffer(data: Option[Vector3f]*): Option[FloatBuffer] = data match {
...
}
This code will not compile due to the two methods having the same method signature. None type would not know which method to call.
I could just rename the methods, however I would like to this overloading style in my code.
After type erasure this two methods become createFloatBuffer(data: Option), and all types information is lost and not available at run time.
As a workaround I can suggest you to use TypeClass pattern.
case class Quaternion(v: Int)
case class Vector3f(v: Int)
case class FloatBuffer(v: Int)
sealed trait FloatBufferBuilder[T] {
def createFloatBuffer(data: Option[T]): Option[FloatBuffer]
}
implicit object QuaternionFloatBufferBuilder extends FloatBufferBuilder[Quaternion] {
def createFloatBuffer(data: Option[Quaternion]) = data.map(d => FloatBuffer(d.v))
}
implicit object Vector3fFloatBufferBuilder extends FloatBufferBuilder[Vector3f] {
def createFloatBuffer(data: Option[Vector3f]) = data.map(d => FloatBuffer(d.v))
}
def createFloatBuffer[T : FloatBufferBuilder](data: Option[T]): Option[FloatBuffer] =
implicitly[FloatBufferBuilder[T]].createFloatBuffer(data)
println(createFloatBuffer(Some(Quaternion(1))))
println(createFloatBuffer(Some(Vector3f(1))))
Magnet Pattern could also interesting for you: http://spray.io/blog/2012-12-13-the-magnet-pattern/
This is the use case for:
scala> object X { def f(is: Int*) = 42 ; def f(ds: Double*) = 43 }
<console>:10: error: double definition:
def f(is: Int*): Int at line 10 and
def f(ds: Double*): Int at line 10
have same type after erasure: (is: Seq)Int
object X { def f(is: Int*) = 42 ; def f(ds: Double*) = 43 }
^
scala> object X { def f(is: Int*) = 42 ; def f(ds: Double*)(implicit dummy: DummyImplicit) = 43 }
defined object X
scala> X f 1
res2: Int = 42
scala> X f 1.0
res3: Int = 43
I'm building a web-application using Play and Slick, and find myself in a situation where the user-facing forms are similar, but not exactly the same as the database model.
Hence I have two very similar case classes, and need to map from one to another (e.g. while filling the form for rendering an "update" view).
In the case I'm interested in, the database model case class is a super-set of the form case-class, i.e. the only difference between both is that the database model has two more fields (two identifiers, basically).
What I'm now wondering about is whether there'd be a way to build a small library (e.g. macro-driven) to automatically populate the form case class from the database case class based on the member names. I've seen that it may be possible to access this kind of information via reflection using Paranamer, but I'd rather not venture into this.
Here is a solution using Dynamic because I wanted to try it out. A macro would decide statically whether to emit an apply of a source value method, the default value method, or just to supply a literal. The syntax could look something like newFrom[C](k). (Update: see below for the macro.)
import scala.language.dynamics
trait Invocable extends Dynamic {
import scala.reflect.runtime.currentMirror
import scala.reflect.runtime.universe._
def applyDynamic(method: String)(source: Any) = {
require(method endsWith "From")
def caseMethod(s: Symbol) = s.asTerm.isCaseAccessor && s.asTerm.isMethod
val sm = currentMirror reflect source
val ms = sm.symbol.asClass.typeSignature.members filter caseMethod map (_.asMethod)
val values = ms map (m => (m.name, (sm reflectMethod m)()))
val im = currentMirror reflect this
invokeWith(im, method dropRight 4, values.toMap)
}
def invokeWith(im: InstanceMirror, name: String, values: Map[Name, Any]): Any = {
val at = TermName(name)
val ts = im.symbol.typeSignature
val method = (ts member at).asMethod
// supplied value or defarg or default val for type of p
def valueFor(p: Symbol, i: Int): Any = {
if (values contains p.name) values(p.name)
else ts member TermName(s"$name$$default$$${i+1}") match {
case NoSymbol =>
if (p.typeSignature.typeSymbol.asClass.isPrimitive) {
if (p.typeSignature <:< typeOf[Int]) 0
else if (p.typeSignature <:< typeOf[Double]) 0.0
else ???
} else null
case defarg => (im reflectMethod defarg.asMethod)()
}
}
val args = (for (ps <- method.paramss; p <- ps) yield p).zipWithIndex map (p => valueFor(p._1,p._2))
(im reflectMethod method)(args: _*)
}
}
case class C(a: String, b: Int, c: Double = 2.0, d: Double)
case class K(b: Int, e: String, a: String)
object C extends Invocable
object Test extends App {
val res = C applyFrom K(8, "oh", "kay")
Console println res // C(kay,8,2.0,0.0)
}
Update: Here is the macro version, more for fun than for profit:
import scala.language.experimental.macros
import scala.reflect.macros._
import scala.collection.mutable.ListBuffer
def newFrom[A, B](source: A): B = macro newFrom_[A, B]
def newFrom_[A: c.WeakTypeTag, B: c.WeakTypeTag](c: Context)(source: c.Expr[A]): c.Expr[B] = {
import c.{ literal, literalNull }
import c.universe._
import treeBuild._
import nme.{ CONSTRUCTOR => Ctor }
def caseMethod(s: Symbol) = s.asTerm.isCaseAccessor && s.asTerm.isMethod
def defaulter(name: Name, i: Int): String = s"${name.encoded}$$default$$${i+1}"
val noargs = List[c.Tree]()
// side effects: first evaluate the arg
val side = ListBuffer[c.Tree]()
val src = TermName(c freshName "src$")
side += ValDef(Modifiers(), src, TypeTree(source.tree.tpe), source.tree)
// take the arg as instance of a case class and use the case members
val a = implicitly[c.WeakTypeTag[A]].tpe
val srcs = (a.members filter caseMethod map (m => (m.name, m.asMethod))).toMap
// construct the target, using src fields, defaults (from the companion), or zero
val b = implicitly[c.WeakTypeTag[B]].tpe
val bm = b.typeSymbol.asClass.companionSymbol.asModule
val bc = bm.moduleClass.asClass.typeSignature
val ps = (b declaration Ctor).asMethod.paramss.flatten.zipWithIndex
val args: List[c.Tree] = ps map { case (p, i) =>
if (srcs contains p.name)
Select(Ident(src), p.name)
else bc member TermName(defaulter(Ctor, i)) match {
case NoSymbol =>
if (p.typeSignature.typeSymbol.asClass.isPrimitive) {
if (p.typeSignature <:< typeOf[Int]) literal(0).tree
else if (p.typeSignature <:< typeOf[Double]) literal(0.0).tree
else ???
} else literalNull.tree
case defarg => Select(mkAttributedRef(bm), defarg.name)
}
}
c.Expr(Block(side.toList, Apply(Select(New(mkAttributedIdent(b.typeSymbol)), Ctor), args)))
}
With usage:
case class C(a: String, b: Int, c: Double = 2.0, d: Double)
case class K(b: Int, e: String, a: String) { def i() = b }
val res = newFrom[K, C](K(8, "oh", "kay"))