To have a better performance, some times it is better to do some computations on compile time and generate code aka quote.
I am only propose to have compile time functions and not to be able to change the syntax.
All parameters are passed similar to Scala call by name.
Also in quote it will not be possibile to use return, so functions only generate code and not change the flow execution.
Could you please provide some specific use cases where you need to perform such computations, and explain why you prefer to perform them on every compilation, rather than calculate the value once and include its result in the source code?
Logging, no need to check logging level, library can do it for you:
logger.debug("Some expensive message!")`
will be rewritten into
if (logger.isDebugEnabled) logger.debug("Some expensive message!")
Querydsl or JOOQ, genereate query string from dsl:
val query = selectFrom(person).where(person.firstName.eq(firstName), person.lastName.eq(lastName));
will be rewritten into
val query = "SELECT * FROM person WHERE first_name=:firstName AND last_name=:lastName";
Routing:
val routes = arrayOf(
get("/hello", (req, res) -> "Hello World");
get("/fizz", (req, res) -> "bazz");
);
dispatch(routes);
will be rewritten into
if (req.path == "/hello") {
"Hello World";
} else if (req.path == "/fizz") {
"bazz"
}
So, it offers possibility to write less and concise code, but the result will be like we will write code without some library.
It will be very useful feature for library writers. You can see quote from Elixir.
These are not compile-time functions, these are macros. While it is possible that macros will be added to Kotlin at some point, we don’t have any plans to work on them in the short or medium term.
Yeap, macros. But with restrictions: not allow syntax change and not allow return in quote.
Also it will be useful to create class based on interface, at the moment we can only do that in run-time using Proxy.
And it will run more faster, because it will be real class and not a proxy.
Also with macros we can reverse routing
You don’t need to explain all the usecases for macros; we’re aware of them. What I wrote above is still true.
There’s a similar feature request regarding compile-time evaluated constant expressions: https://youtrack.jetbrains.com/issue/KT-14652
Why don’t implement logging as a high order function?
log.debug {
//Some expensive computations
“Some message”
}
where block evaluated only when debug level allowed.
Other examples also strange. The SQL construction you can put into lazy field. The dispatching may just use cycle, why your suggestion better?
For example, I have a data class
data class DayChartPoint(
val date: Date,
val open: Double,
val high: Double,
val low: Double,
val close: Double,
val volume: Long,
val unadjustedVolume: Long,
val change: Double,
val changePercent: Double,
val vwap: Double,
val label: String,
val changeOverTime: Double
)
and I have some other entity that should consume this data. For example, JavaFx, which expects the fields to be observable values. So one must have another class defined:
class DayChartPointBean {
val changeOverTimeProperty = SimpleDoubleProperty()
var changeOverTime by changeOverTimeProperty
val labelProperty = SimpleStringProperty()
var label by labelProperty
val vwapProperty = SimpleDoubleProperty()
var vwap by vwapProperty
val changePercentProperty = SimpleDoubleProperty()
var changePercent by changePercentProperty
val changeProperty = SimpleDoubleProperty()
var change by changeProperty
val unadjustedVolumeProperty = SimpleLongProperty()
var unadjustedVolume by unadjustedVolumeProperty
val volumeProperty = SimpleLongProperty()
var volume by volumeProperty
val closeProperty = SimpleDoubleProperty()
var close by closeProperty
val lowProperty = SimpleDoubleProperty()
var low by lowProperty
val highProperty = SimpleDoubleProperty()
var high by highProperty
val openProperty = SimpleDoubleProperty()
var open by openProperty
val dateProperty = SimpleObjectProperty<Date>()
var date by dateProperty
}
and then have a routine defined as well, that would perform a conversion from one data type to another:
fun DayChartPoint.toBean() =
DayChartPointBean().let {
it.change = this.change
it.changeOverTime = this.changeOverTime
it.changePercent = this.changePercent
it.close = this.close
it.date = this.date
it.high = this.high
it.label = this.label
it.low = this.low
it.open = this.open
it.unadjustedVolume = this.unadjustedVolume
it.volume = this.volume
it.vwap = this.vwap
it
}
Going through the chore of creating this even once is not really what I’m looking for. As well as changing this all by hand in all places, if something in the data definition changes.
And if I want to use JavaFx, there’s nothing that I can do, but to provide the data in the format that framework expects. Other, non-JavaFx, examples abound.
If the DayChartPointBean
class and DayChartPoint.toBean()
function definition could be generated at compile time, this would certainly save me a lot of time and give me a lot of confidence that once changes to the DayChartPoint
were made, corresponding classes and methods would be updated and updated correctly.
You can use annotation processors to generate those classes; all necessary support for it is available in Kotlin today.
Sort of. Annotation processors are quite complicated to set up and are target (JVM) specific.
EDIT: You also have to use Java reflection API inside annotation processors to get info about Kotlin classes, AFAIK. Which is awkward:
data class Person(
val name: String,
var age: Int
)
Person::class.java.declaredMethods.forEach {
println("${it.name}: ${it.returnType}")
}
prints
equals: boolean
toString: class java.lang.String
hashCode: int
getName: class java.lang.String
copy: class com.vitalyk.kotlin.sandbox.LanguageKt$javaReflection$Person
setAge: void
getAge: int
component1: class java.lang.String
component2: int
copy$default: class com.vitalyk.kotlin.sandbox.LanguageKt$javaReflection$Person
Using Kotlin’s reflection API
Person::class.memberProperties.forEach {
println("${ if (it is KMutableProperty<*>) "var" else "val" } ${it.name}: ${it.returnType}")
}
we get
var age: kotlin.Int
val name: kotlin.String
So it’s much easier to generate new Kotlin classes, since you don’t have to guess which Java methods were generated from which Kotlin properties of the original Kotlin class.
There’s also a relative dearth of information and documentation about kapt.
Yeah, annotation processing has been pretty much a nightmare for me these past couple days. I can’t really call it a clean solution. Currently stuck at roundEnv.rootElements
not listing data classes, only regular Kotlin classes. Kotlin’s own reflection is so much cleaner and easier to use. There’s got to be a better way.
+1 to this suggestion.
Totally agree with @yay ^
Is this discussion moved to Youtrack already? Or this is still the correct place?