Finding the actual type in macro - scala

I am trying to write a macro that extracts the type information. Here is a minimized version.
class Extractor[E] {
def extract(entity: E): Unit = macro ExtractorImpl.extractImpl[E]
}
object ExtractorImpl {
def extractImpl[E: c.WeakTypeTag](c: Context)(entity: c.Expr[E]): c.Tree = {
import c.universe._
val actualType = implicitly[WeakTypeTag[E]].tpe
c.info(c.enclosingPosition, actualType.toString, true)
q"{}"
}
}
Here is a typical implementation of Extractor:
case class Person(name: String)
object PersonExtractor extends Extractor[Person]
If I use this class directly as in PersonExtractor.extract(new Person("test name")), I get Person printed as info. But, if I use it indirectly as follows, it prints only E:
class Mapper[E](extractor: Extractor[E]) {
def extract(e: E) = extractor.extract(e)
}
class PersonMapper extends Mapper[Person](new Extractor[Person])
class Test {
new PersonMapper().extract(new Person("test name"))
}
How do I go about getting the Person type available as actualType?

use implicit macro
trait Extractor[E] {
def extract(entity: E): Unit
}
object Extractor {
implicit def i[E] : Extractor[E] = macro ExtractorImpl.extractImpl[E]
}
object ExtractorImpl {
def extractImpl[E: c.WeakTypeTag](c: Context):c.Tree = {
import c.universe._
val actualType = c.weakTypeOf[E]
c.info(c.enclosingPosition, actualType.toString, false)
q"""
new Extractor[$actualType]{
def extract(entity: $actualType): Unit = println("hello world")
}
"""
}
}
// test
case class Person(name: String)
//object PersonExtractor extends Extractor[Person]
class Mapper[E] {
def extract(e: E)(implicit extractor: Extractor[E]) = extractor.extract(e)
}
class PersonMapper extends Mapper[Person]
class Test {
new PersonMapper().extract(new Person("test name")) // show info:Person
}
Edit:
//test2
class Mapper2[E](extractor: Extractor[E]) {
def extract(e: E) = extractor.extract(e)
}
object Mapper2 extends Mapper2[Person](Extractor.i[Person])//also show info:Person

Related

using enumeratum enum as BSONDocument value does not compile

when I try to wrap my query in BSONDocument and putting my enumeratum enum as the value it docent compile.
for example, my enum:
sealed trait ProcessingStatus extends EnumEntry with UpperSnakecase
object ProcessingStatus extends Enum[ProcessingStatus] with ReactiveMongoBsonEnum[ProcessingStatus] {
val values: IndexedSeq[ProcessingStatus] = findValues
case object Processing extends ProcessingStatus
case object Done extends ProcessingStatus
}
and I have play json serializer that explains how to serialize:
object JsonSerialization {
import reactivemongo.api.bson._
implicit object ProcessingStatusReader extends BSONReader[ProcessingStatus] {
override def readTry(bson: BSONValue): Try[ProcessingStatus] = bson match {
case BSONString(s) => bson.asTry[ProcessingStatus]
case _ => Failure(new RuntimeException("String value expected"))
}
}
implicit object ProcessingStatusWriter extends BSONWriter[ProcessingStatus] {
override def writeTry(t: ProcessingStatus): Try[BSONString] = Try(BSONString(t.entryName))
}
//Report Serializers
implicit val ProcessingStatusFormat: Format[ProcessingStatus] = EnumFormats.formats(ProcessingStatus)
implicit val ReportFormat: OFormat[Report] = Json.format[Report]
}
and now in my dao this does not compile:
import reactivemongo.play.json.compat.json2bson.{toDocumentReader, toDocumentWriter}
import serializers.JsonSerialization._
def findReport(reportId: String) = {
val test = BSONDocument("123" -> ProcessingStatus.Processing) // dosent compile
}
screenshot:
compilation error:
overloaded method apply with alternatives:
(elms: Iterable[(String, reactivemongo.api.bson.BSONValue)])reactivemongo.api.bson.BSONDocument <and>
(elms: reactivemongo.api.bson.ElementProducer*)reactivemongo.api.bson.BSONDocument
cannot be applied to ((String, enums.ProcessingStatus.Done.type))
val test = BSONDocument("status" -> ProcessingStatus.Done)
An IDE error is not a compilation error (recommend to use sbt and its console to tests).
Your code (simplified as bellow), is compiling fine, whatever is telling the IDE (which is wrong).
import reactivemongo.api.bson._
import scala.util.Try
trait ProcessingStatus {
def entryName = "foo"
}
object JsonSerialization {
implicit object ProcessingStatusWriter extends BSONWriter[ProcessingStatus] {
override def writeTry(t: ProcessingStatus): Try[BSONString] = Try(BSONString(t.entryName))
}
}
import JsonSerialization._
BSON.write(new ProcessingStatus {})
Note.1: writeTry doesn't override anything, so the modifier is useless (and can lead to missunderstanding).
Note.2: Try(..) with a pure value such as BSONString(t.entryName) is over-engineered, rather use Success(..).
Note.3: Convenient factories are available such as val w = BSONWriter[T] { t => ... }.
Edit:
The typeclass BSONWriter (as most typeclass) is invariant, so having a BSONWriter[T] in the implicit scope doesn't allow to resolve a BSONWriter[U] forSome { U <: T }.
trait ProcessingStatus {
def entryName: String
}
object ProcessingStatus {
case object Done extends ProcessingStatus { val entryName = "done" }
}
object JsonSerialization {
implicit object ProcessingStatusWriter extends BSONWriter[ProcessingStatus] {
override def writeTry(t: ProcessingStatus): Try[BSONString] = Try(BSONString(t.entryName))
}
}
import JsonSerialization._
BSON.write(ProcessingStatus.Done
/*
<console>:32: error: could not find implicit value for parameter writer: reactivemongo.api.bson.BSONWriter[ProcessingStatus.Done.type]
BSON.write(ProcessingStatus.Done)
*/
// --- BUT ---
BSON.write(ProcessingStatus.Done: ProcessingStatus)
// Success(BSONString(done))
Also exposing Done (and other cases) as ProcessingStatus in the API is working.
import reactivemongo.api.bson._
import scala.util.Try
sealed trait ProcessingStatus {
def entryName: String
}
object ProcessingStatus {
val Done: ProcessingStatus = new ProcessingStatus { val entryName = "done" }
}
object JsonSerialization {
implicit object ProcessingStatusWriter extends BSONWriter[ProcessingStatus] {
override def writeTry(t: ProcessingStatus): Try[BSONString] = Try(BSONString(t.entryName))
}
}
import JsonSerialization._
BSON.write(ProcessingStatus.Done)

Scala: Not able to find implicit from object

I have the following scala code. I don't understand why the implicit is not being figured by the compiler. I also tried putting the import line inside Main. Note however that when the implicit object was created inside Main, then the code ran correctly
import LoggingAddon._
object Main {
def main(args: Array[String]): Unit = {
val dog = new Dog
Util.act(dog)
}
}
class Dog {
def bark(): Unit = {
println("woof")
}
}
trait Action[A] {
def action(x: A): Unit
}
trait WithoutLogging[A] extends Action[A] {
}
trait WithLogging[A] extends Action[A] {
}
object LoggingAddon {
implicit object DogWithLogging extends WithLogging[Dog] {
override def action(x: Dog): Unit = {
println("before")
x.bark()
print("after")
}
}
}
object NoLoggingAddion {
implicit object DogWithoutLogging extends WithoutLogging[Dog] {
override def action(x: Dog): Unit = {
x.bark()
}
}
}
object Util {
def act(x: Dog)(implicit nolog: Action[Dog]): Unit = {
nolog.action(x)
}
}
I have imported the necessary implicit from the LoggingAddon but still the scala compiler says could not find implicit Action[Dog]
All I'm trying to do is make a pluggable typeclass. Rather than changing any piece of code, merely change the import statements to have different side effects
Simply move the order of usage where implicit is imported, I moved to the bottom in following example
class Dog {
def bark(): Unit = {
println("woof")
}
}
trait Action[A] {
def action(x: A): Unit
}
trait WithoutLogging[A] extends Action[A] {
}
trait WithLogging[A] extends Action[A] {
}
object LoggingAddon {
implicit object DogWithLogging extends WithLogging[Dog] {
override def action(x: Dog): Unit = {
println("before")
x.bark()
print("after")
}
}
}
object NoLoggingAddion {
implicit object DogWithoutLogging extends WithoutLogging[Dog] {
override def action(x: Dog): Unit = {
x.bark()
}
}
}
object Util {
def act(x: Dog)(implicit nolog: Action[Dog]): Unit = {
nolog.action(x)
}
}
import LoggingAddon._
object Main {
def main(args: Array[String]): Unit = {
val dog = new Dog
Util.act(dog)
}
}

illegal inheritance; self-type .... does not conform to

I am trying to implement a cake pattern like code, but I get:
Error:(47, 36) illegal inheritance;
self-type app.server.im.Im_Api_Service_Impl.type does not conform to app.server.im.Persistence[app.server.im.State.State]'s selftype app.server.im.Persistence[app.server.im.State.State] with app.server.im.Persistable[app.server.im.State.State]
object Im_Api_Service_Impl extends Persistence[State.State]
with the code below.
What might be the problem ?
I looked at this SOF question, but it seems to describe a different scenerio (or maybe I am just not seeing the similarity).
import app.shared.IndexChange
import app.shared.apiAndModel.im.{CanCreateEntity, Im_Api_Interface, LineShared, LineSharedPayload, UUID}
import upickle.default._
object State {
type State = Seq[LineShared]
}
trait Im_Api_Service extends Im_Api_Interface with Persistable[State.State] {
implicit object CanCreate extends CanCreateEntity
import State.State
val init = List("egy", "ketto", "harom", "negy", "ot", "hat", "het", "nyolc").map(LineSharedPayload(_)).map(LineShared(_))
val fileNameForPersistence="state"
var state: State = init
def setState(s:State)={state=s}
override def getLines(): Seq[LineShared] = state
def moveLine(ls: State, from: Int, to: Int): Seq[LineShared] = {
val r=IndexChange(from, to).updatedList[LineShared](ls.toList)
r
}
override def moveLine(from: Int, to: Int): Seq[LineShared] = {state=moveLine(state, from, to);state;}
override def newLine(pl: LineSharedPayload): Seq[LineShared] = {
setState(state :+ LineShared(pl))
state
}
def getLine(ls:State, id: UUID): Option[LineShared] = ls.find(_.id == id)
override def getLine(id: UUID): Option[LineShared] = getLine(state,id)
override def updateLine(l: LineShared): State = {
setState(state.map(ll => if (ll~l) l else ll ))
state
}
}
object Im_Api_Service_Impl extends Persistence[State.State]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
illegal inheritance problem HERE
trait Persistable[State]
{
val fileNameForPersistence:String
var state :State
}
trait Persistence[State] { this:Persistable[State] =>
def serializeState(s: State): String = write(s)
// TODO add timestamp to filename
def saveState(): Unit = writeToFile(serializeState(state), fileNameForPersistence)
def loadState(): Unit = {
try {
state = deserializeState(readFromFile(fileNameForPersistence))
}
catch {
case e: Exception => println(e)
}
}
def writeToFile(s: String, fn: String): Unit = {
import java.io._
val pw = new PrintWriter(new File(fn))
pw.write(s)
pw.close
}
def readFromFile(fn: String): String = {
val source = scala.io.Source.fromFile(fn)
val lines: String = try source.mkString finally source.close()
lines
}
def deserializeState(s: String): State = read[State](s)
}
You have promised that trait Persistence would only be mixed into a class that extends Persistable: trait Persistence[State] { this:Persistable[State].
Instead, your object extends Persistence[State.State]. Is that the same thing?

Scala type class can't find implicit instances

I am trying to create a type class in Scala and use it to make a simple polymorphic case class. This example doesn't compile and gives "could not find implicit value for parameter writer:A$A228.this.ValueWriter[T]". I can't really figure out what could be going wrong or where to start.
trait Keeper
case class StringKeeper(measure: String) extends Keeper
case class StringLengthKeeper(measure: Int) extends Keeper
trait ValueWriter[A] {
def write(value: String): A
}
object DefaultValueWriters {
implicit val stringWriter = new ValueWriter[StringKeeper] {
def write(value: String) = StringKeeper(value)
}
implicit val stringLengthWriter = new ValueWriter[StringLengthKeeper] {
def write(value: String) = StringLengthKeeper(value.length)
}
}
object Write {
def toWrite[A](value: String)(implicit writer: ValueWriter[A]) = {
writer.write(value)
}
}
case class WriterOfKeepers[T <: Keeper](value: String) {
def run: T = {
Write.toWrite[T](value)
}
}
import DefaultValueWriters._
val writerLengthKeeper = WriterOfKeepers[StringLengthKeeper]("TestString")
writerLengthKeeper.run
There is no implicit ValueWriter in scope when you call Write.toWrite.
You need to have the implicit parameter in the constructor
case class WriterOfKeepers[T <: Keeper : ValueWriter](value: String) {
def run: T = {
Write.toWrite[T](value)
}
}
or in the method
case class WriterOfKeepers[T <: Keeper](value: String) {
def run(implicit writer: ValueWriter[T]): T = {
Write.toWrite(value)
}
}
or find some other way compatible with your requirements (and I don't know those).

Scala, typeclass and "could not find implicit value"

I am facing some weird problem with typeclass below: for some reason implicit object ContentUploader is not resolved on call to upload method of DemoActor.
import akka.actor.Actor
import java.io.File
import org.slf4j.LoggerFactory
class DemoActor extends Actor {
import DemoActor.UploaderImpl._
override def receive = {
case (x: DemoActor.Content) =>
DemoActor.upload(x)
}
}
object DemoActor {
val LOG = LoggerFactory.getLogger("DemoActor")
sealed trait UploadData {
val data: Array[File]
}
case class Content(data: Array[File]) extends UploadData
case class UploadResult(url: String, contentType: String, size: Long)
trait S3Uploader[T <: UploadData] {
def uploadToS3(filez: Array[File]): Iterable[UploadResult]
}
object UploaderImpl {
val LOG = LoggerFactory.getLogger("Uploader")
private def contentType(name: String): String = {
"application/octet-stream"
}
private def doUpload(filez: Array[File], bucketName: String) = {
LOG.debug("Uploading: {} to {}", filez, bucketName)
filez.flatMap {
case f =>
try {
val key = f.getName
val mime = contentType(f.getName)
Some(UploadResult("http://" + bucketName + ".s3.amazonaws.com/" + key, mime, f.length()))
} catch {
case e =>
LOG.error("Can not upload", e)
None
}
}
}
implicit object ContentUploader extends S3Uploader[Content] {
lazy val bucketName = "resources.aws.bucketname"
lazy val awsSecret = "resources.aws.secret.key"
lazy val awsAccess = "resources.aws.access.key"
override def uploadToS3(filez: Array[File]) = doUpload(filez, bucketName)
}
}
def upload[T <: UploadData](src: T)(implicit uploader: S3Uploader[T]) = uploader.uploadToS3(src.data)
}
What have I missed here?
UPD
if I move definition of class for DemoActor inside object DemoActor, like
import akka.actor.Actor
import java.io.File
import org.slf4j.LoggerFactory
object DemoActor {
val LOG = LoggerFactory.getLogger("DemoActor")
sealed trait UploadData {
val data: Array[File]
}
case class Content(data: Array[File]) extends UploadData
case class UploadResult(url: String, contentType: String, size: Long)
trait S3Uploader[UploadData] {
def uploadToS3(filez: Array[File]): Iterable[UploadResult]
}
object UploaderImpl {
val LOG = LoggerFactory.getLogger("Uploader")
private def contentType(name: String): String = {
"application/octet-stream"
}
private def doUpload(filez: Array[File], bucketName: String) = {
LOG.debug("Uploading: {} to {}", filez, bucketName)
filez.flatMap {
case f =>
try {
val key = f.getName
val mime = contentType(f.getName)
Some(UploadResult("http://" + bucketName + ".s3.amazonaws.com/" + key, mime, f.length()))
} catch {
case e =>
LOG.error("Can not upload", e)
None
}
}
}
implicit object ContentUploader extends S3Uploader[DemoActor.Content] {
lazy val bucketName = "resources.aws.bucketname"
lazy val awsSecret = "resources.aws.secret.key"
lazy val awsAccess = "resources.aws.access.key"
override def uploadToS3(filez: Array[File]) = doUpload(filez, bucketName)
}
}
def upload[T <: UploadData](src: T)(implicit uploader: S3Uploader[T]) = uploader.uploadToS3(src.data)
class DemoActor extends Actor {
import DemoActor.UploaderImpl._
override def receive = {
case (x: DemoActor.Content) =>
DemoActor.upload(x)
}
}
}
then everything works well. Are there some issues with namespacing?
It is not finding it because implicit forward references must be explicitly typed to be considered, and this one isn't.
If this is confusing, maybe two ways of fixing it might make it clear. First, you can declare the type of the implicit. Remove the implicit from the object, and declare a val pointing to it:
implicit val contentUploader: S3Uploader[DemoActor.Content] = ContentUploader
The second way is moving the class DemoActor declaration to the end of the file, so it stays after the the object DemoActor declaration.
The reason it works like this is that the compiler must search for the implicit before the rest of the file is fully typed, so it doesn't know, at that time, that object ContentUploader satisfy the search.