I liked the idea of using
fields macro in scala so much so I created a dedicated project for this macro to start reusing it in the different projects I did.
Here it is.
Few words about implementation of this macro
The code has been reworked and now it is possible to transform field value using a supplied function. It means that Field and Fields types, and fields macro signatures are changed. In addition, there is a new convenient macro called allFields. I.e.:
case class Field[-I <: AnyRef, +R, +A <: Product](name: String, get: I => R, args: A)
type Fields[-I <: AnyRef, +R, +A <: Product] = List[Field[I, R, A]]
def fields[Ann, I <: AnyRef, R, Args <: Product](apply: Symbol) = macro fieldsImpl[Ann, I, R, Args]
def allFields[I <: AnyRef, R](apply: Symbol) = macro fieldsImpl[Any, I, R, None.type]
allFields is the new function that lists all public value members enclosed in the given type regardless of annotations.
R in a Field(s) type stands for RETURN and represents type of a value returned by the field getter transformed using the supplied function. Value transformer function is passed into the macro using its symbol. You might wonder why aren't we just using something like
f :
T => R forSome {
type T } ? That's because you can't pass the following function that way:
def trans[T : TypeClass](x : T) : Int = ???
(which is just a sweet way of saving some keystrokes by not writing this:)
def trans[T](x : T)(implicit tc : TypeClass[T]) : Int = ???
i.e. you can't pass function with TWO parameters (one of which is implicit parameter) where a ONE parameter function is expected. That is quite obvious but anyway.. So we use Symbol (in the spirit of Lisp). As you might guess by looking at the snippet below, it is expected that the symbol is constructed directly and not passed by reference:
val applyFunName =
apply.tree match {
case Apply(_, List(Literal(Constant(s)))) => s.toString
case _ =>
c.abort(apply.tree.pos,
"fields macro is expected to be used with symbol literal like 'nothing or 'myFunction")
}
Almost everything in the macro implementation remains more-less same, except part that constructs expression for getting field value out of object. Now it looks like this:
val applyFunTree = c.parse(applyFunName)
val getFunArgTree = ValDef(Modifiers(), newTermName("x"), TypeTree(instanceT), EmptyTree)
val getFunBodyTree =
treeBuild.mkMethodCall(applyFunTree,
List(Select(Ident(newTermName("x")), newTermName(name)),
argsTree))
getFunBodyTree illustrates what signature is really expected for the transformer function: in addition to field value, all arguments of the annotation are passed into the function (or None if no annotation used or annotation has no arguments). For example, you can't use Predef.identity function, instead, you should use valueIdentity which is (already) defined like this:
def valueIdentity[X] (value : X, annotationArgs : Any) : X = value
Having annotation arguments provided for the currently processing field gives you possibility for further customization of how the value is transformed. Now lets do an example.
Real-world example
Suppose you want to serialize your custom classes into JSON with no boilerplate code what so ever. That is how you can do it with this only (general-purpose) macro. Lets define some generic Writes typeclase provider:
implicit def writesForFields[T <: AnyRef](implicit fields: clazz.Fields[T, JsValue, _]): Writes[T] = {
new Writes[T] {
def writes(t: T): JsValue = {
JsObject(fields map {
(field: clazz.Field[T, JsValue, _]) =>
field.name -> field.get(t: T)
})
}
}
}
The function shown above implicitly creates Writes for any type T which has an implicit instance of type Fields[T, JsValue, _] (read it like
"List of fields of class T along with function to get value of type JsValue for each field"). Now lets define the transformer function, it will be used for serialization of field values:
def asJsValue[T : Writes](v : T, annArgs : Any) : JsValue = Json.toJson(v)
That was the only code needed to bootstrap your mini-serialization framework. Now you can use it. Lets assume you have declarations:
case class JquerySocketEvent[T](id: Int, data: T, `type`: String = "message", reply: Boolean = false)
case class ChatMessage(user: String, text: String)
implicit def jquerySocketEventJsFields[T: Writes] = clazz.allFields[JquerySocketEvent[T], JsValue]('asJsValue)
implicit val chatMessageJsFields = clazz.allFields[ChatMessage, JsValue]('asJsValue)
That's it. ... some fun:
val event = JquerySocketEvent(id = 1, data = ChatMessage("Fluttershy", "Yay!"))
println (Json.toJson(event))
... prints:
{"id":1,"data":{"user":"Fluttershy","text":"Yay!"},"type":"message","reply":false}
That was easy enough. Feel free to use it, re-implement it or implement a more powerful stuff. Macros FTW!
You can not imagine just how much time I
ReplyDeletehad spent for this info! Thanks! vivi winkler
no deposit bonus forex 2021 - takipçi satın al - takipçi satın al - takipçi satın al - tiktok takipçi satın al - instagram beğeni satın al - instagram beğeni satın al - google haritalara yer ekleme - btcturk güvenilir mi - izlenme-satin-al.com - numarasmsonay.com - borsagazete.com - takipcisatinals.com - izlenme-satin-al.com/youtube - google haritalara yer ekleme - altyapısız internet - mikrofiber havlu - forexbonus2020.com - tiktok jeton hilesi - tiktok beğeni satın al - microsoft word ücretsiz indir - misli apk indir - binance güvenilir mi - takipçi satın al - mikrofiber havlu - uc satın al - takipçi satın al - takipçi satın al - finanspedia.com
ReplyDeleteinstagram takipçi satın al
ReplyDeleteinstagram takipçi satın al
takipçi satın al
instagram takipçi satın al
instagram takipçi satın al
takipçi satın al
instagram takipçi satın al
aşk kitapları
tiktok takipçi satın al
instagram beğeni satın al
youtube abone satın al
twitter takipçi satın al
tiktok beğeni satın al
tiktok izlenme satın al
twitter takipçi satın al
tiktok takipçi satın al
youtube abone satın al
tiktok beğeni satın al
instagram beğeni satın al
trend topic satın al
trend topic satın al
youtube abone satın al
beğeni satın al
tiktok izlenme satın al
sms onay
youtube izlenme satın al
tiktok beğeni satın al
sms onay
sms onay
perde modelleri
instagram takipçi satın al
takipçi satın al
tiktok jeton hilesi
pubg uc satın al
sultanbet
marsbahis
betboo
betboo
betboo
marsbahis
ReplyDeletebetboo
sultanbet
marsbahis
betboo
sultanbet
seo fiyatları
ReplyDeletesaç ekimi
dedektör
instagram takipçi satın al
ankara evden eve nakliyat
fantezi iç giyim
sosyal medya yönetimi
mobil ödeme bozdurma
kripto para nasıl alınır
SMM PANEL
ReplyDeletesmm panel
https://isilanlariblog.com/
instagram takipçi satın al
hirdavatciburada.com
beyazesyateknikservisi.com.tr
servis
TİKTOK HİLE İNDİR
üsküdar alarko carrier klima servisi
ReplyDeletebeykoz daikin klima servisi
çekmeköy toshiba klima servisi
ataşehir toshiba klima servisi
çekmeköy beko klima servisi
ataşehir beko klima servisi
maltepe lg klima servisi
kadıköy lg klima servisi
maltepe alarko carrier klima servisi
Good content. You write beautiful things.
ReplyDeletekorsan taksi
sportsbet
vbet
mrbahis
taksi
sportsbet
vbet
hacklink
mrbahis
Good text Write good content success. Thank you
ReplyDeletepoker siteleri
tipobet
mobil ödeme bahis
betmatik
kibris bahis siteleri
kralbet
betpark
bonus veren siteler