Kotlin extensions method - kotlin

How can I write extension function in kotlin for such function:
public abstract fun <T : TargetValue> getAverage(metric: StatisticsMetric,
interval: DateInterval,
timeUnit: TimeUnit,
mode: GroupingMode /* = compiled code */,
result: (AsyncResult<kotlin.collections.Map<Info, T>, Error>) -> kotlin.Unit): kotlin.Unit
To be able call it like this:
TargetValueV2 : TargetValue
"This function about signature".asFlow(): Flow<MyCustomDataEvent>
val flowResult = sdk.getAverage<TargetValueV2>(
metric = TIME,
interval = DateInterval(startDay, currentDate),
timeUnit = DAY,
mode = private
).asFlow()

Related

Kotlin: How to use higher order functions with OpenEndRange<LocalDate>

I want to use higher order functions like map for open end ranges.
val from = LocalDate.now().minusDays(10)
val to = LocalDate.now()
(from ..< to).forEach(::println)
I tried to copy an example for ClosedRange<LocalDate> but it does not work.
package de.otto.di.extensions
import java.time.LocalDate
class OpenEndRangeLocalDateIterator(
startDate: LocalDate,
private val endExclusive: LocalDate,
private val stepDays: Long
) : Iterator<LocalDate> {
private var currentDate = startDate
override fun hasNext() = currentDate.plusDays(stepDays) <= endExclusive
override fun next(): LocalDate {
val next = currentDate
currentDate = currentDate.plusDays(stepDays)
return next
}
}
#OptIn(ExperimentalStdlibApi::class)
class OpenEndLocalDateRange(
override val start: LocalDate,
override val endExclusive: LocalDate,
private val stepDays: Long = 1
) : Iterable<LocalDate>, OpenEndRange<LocalDate> {
override fun iterator(): Iterator<LocalDate> =
OpenEndRangeLocalDateIterator(start, endExclusive, stepDays)
infix fun step(days: Long) = OpenEndLocalDateRange(start, endExclusive, days)
}
infix operator fun LocalDate.rangeUntil(to: LocalDate): OpenEndLocalDateRange =
OpenEndLocalDateRange(this, to)
It is implemented for Int so I assume it must be possible somehow. How can I achieve this?
The issue here is that you've defined the operator function to return OpenEndRange<LocalDate> rather than OpenEndedLocalDateRange. If you change the return type of your operator function that should fix the issue.
The reason why it isn't working as is is because OpenEndRange doesn't have the higher order functions defined for it (ClosedRange doesn't have them defined as well). Int has it because the operators return an IntRange which indirectly extends Iterable<Int> via IntProgression and Iterable has these higher order functions defined, so, the only missing piece is failing to return the correct type from your operator function.

How to create a wrapper higher order function that generates the version with guard clauses of the given given function?

import java.lang.Exception
data class Person(val name: String, val age: Int, val money: Int)
fun drinkBeer(person: Person) {
println("${person.name} drunk beer.")
}
fun <T, S : Exception> enforcer(guardClause: Pair<(args: T) -> Boolean, S>, block: (args: T) -> Unit) =
{ args: T ->
val (condition, exception) = guardClause
if (condition(args)) throw exception
block(args)
}
class UnderAgeException : Exception()
class InsufficientMoneyException : Exception()
fun main() {
val person1 = Person("john", 18, 4000)
val person2 = Person("philips", 17, 5000)
val drinkBeerIfAgeOver18 = enforcer({ it: Person -> it.age < 18 } to UnderAgeException(), ::drinkBeer)
val drinkBeerIfAgeOver18AndMoneyGreaterThan3000 =
enforcer({ it: Person -> it.money < 3000 } to InsufficientMoneyException(), drinkBeerIfAgeOver18)
drinkBeerIfAgeOver18(person1)
drinkBeerIfAgeOver18AndMoneyGreaterThan3000(person2)
}
The intention is evident above. I want to create a higher-level function, say it is called enforcer. It simply works as intended but it falls short if it receives a function whose arity is more than 1. Lastly, the compiler cannot determine the type of parameter being sent over to the first lambda function wrapped in a pair despite that it is signed to have T as the type. The compiler pleases you to declare the type as in the case that { it: Person -> it.money < 3000 }. If I can eliminate those drawbacks, it would be a great productivity booster in projects.

Kotlin Lambda with generic input parameter gets inferred to Nothing when stored in a collection

I have the following classes
class EventHandler<A : Aggregate, E : Event<A>>(
val eventClazz: KClass<out Event<A>>,
val handle: suspend (E) -> Unit
)
class Projection(vararg handlers: EventHandler<out Aggregate, out Event<out Aggregate>>) {
val handlers = handlers.asList()
}
Now I want to invoke the handlers handle function. The Handler is stored in a Projections handlers list.
val subscriptionFilterBuilder = SubscriptionFilterBuilder()
projections.forEach { projection ->
projection.handlers.forEach { handler: EventHandler<out Aggregate, out Event<out Aggregate>> ->
val eventType = getEventType(handler.eventClazz)
val filter = subscriptionFilterBuilder.withEventTypePrefix(eventType).build()
val options = SubscribeToAllOptions.get().filter(filter)
val handlerTarget: KClass<out Event<out Aggregate>> = handler.eventClazz
coroutineScope.launch {
eskWrapper.subscribeToAll(
options = options,
) { subscription, event ->
val deserialized: Event<out Aggregate> =
serDes.deserialize2(event.originalEvent).eventPayload
coroutineScope.launch {
handler.handle(deserialized)
}
}
}
}
}
The compiler gives me the error Type mismatch: inferred type is Event<out Aggregate> but Nothing was expected for the line handler.handle(deserialized).
I guess this is because of the way I store the handlers in the Projection but I don't know how to solve this.
I found a way to ensure type safety in my case.
I can add a function to the EventHandler which takes care of deserialization and invoking the actual handling function. Since this handleWithSer function (don't use this name) is aware of the generic types this approach works.
class EventHandler<A : Aggregate, E : Event<A>>(
val eventClazz: KClass<out Event<A>>,
val handle: suspend (E) -> Unit,
) {
val handleWithSer: suspend (RecordedEvent, EventPayloadSerDes) -> Unit =
{ event: RecordedEvent, serder: EventPayloadSerDes ->
val event = serder.deserialize<A, E>(event)
handle(event.eventPayload)
}
}
If there are other ways to get the correct type from the collection let me know.

Kotlin DSL - union structure

I am designing a DSL and run into a requirement where I have a variable which could be assigned to different ways. Greatly simplified, I would like to set value property either by an integer or by an expression in String. (The real need is even more complex.)
I would like to write in my DSL:
value = 42
or
value = "6*7"
Behind the scene, the value will be stored in a DynamicValue<Int> structure which contains either an integer or the expression.
class DynamicValue<T>(dv : T?, expr : String) {
val directValue : T? = dv
val script : String? = expr
...
}
I tried several ways (delegate, class, etc), but none of them provided these syntax.
Is there a way to declare this union like structure?
What do you think about the following syntax:
value(42)
value("6*7")
//or
value+=42
value+="6*7"
You can do this with operator functions:
class DynamicValue<T>() {
var dv: T? = null
var expr: String? = null
operator fun invoke(dv : T) {
this.dv = dv
this.expr = null
}
operator fun invoke(expr: String) {
this.dv = null
this.expr = expr
}
operator fun plusAssign(dv : T) {
this.dv = dv
this.expr = null
}
operator fun plusAssign(expr: String) {
this.dv = null
this.expr = expr
}
}
You can't redefine the assign operator in Kotlin, therefor the pure syntax value=42 is not possible.
But I wouldn't go with operator functions, it's to magical. I would do this:
val value = DynamicValue<Int>()
value.simple=42
value.expr="6*7"
class DynamicValue2<T>() {
private var _dv: T? = null
private var _expr: String? = null
var simple: T?
get() = _dv
set(value) {
_dv = value
_expr = null
}
var expr: String?
get() = _expr
set(value) {
_expr = value
_dv = null
}
}
Rene's answer gave me the lead and finally I turned up with this solution.
In this solution I took all my requirements in (the ones I dropped out in my original question) so this became much more complicated than my original question would have required.
My whole requirement was to be able to add static values or scripts (snippets) running on a well guarded context. These script would be stored, and executed later. I wanted to enable the whole power of the IDE when writing the script, but would like to guard my scripts from code injections and help the user to use only the context values the script requires.
The trick I used to achieve this is to enable adding script in kotlin, but before I run the whole DSL script and create the business objects, I convert the script into a string. (This string will be executed later in a guarded, wrapped context by JSR233 engine.) This conversation forced me to tokenize the whole script before execution and search/replace some of the tokens. (The whole tokenizer and converter is rather long and boring, so I won't insert here.)
First approach
What my goal was to be able to write any of this:
myobject {
value = static { 42 } // A static solution
value = static { 6 * 7 } // Even this is possible
value = dynamic{ calc(x, y) } // A pure cotlin solution with IDE support
value = dynamic("""calc(x * x)""") // This is the form I convert the above script to
}
where calc, x and y are defined in the context class:
class SpecialScriptContext : ScriptContextBase() {
val hello = "Hello"
val x = 29
val y = 13
fun calc(x: Int, y: Int) = x + y
fun greet(name: String) = println("$hello $name!")
}
So let's see the solution! First I need a DynamicValue class to hold one of the values:
class DynamicValue<T, C : ScriptContextBase, D: ScriptContextDescriptor<C>>
private constructor(val directValue: T?, val script: String?) {
constructor(value: T?) : this(value, null)
constructor(script: String) : this(null, script)
}
This structure will ensure that exactly one of the options (static, script) will be set. (Don't bother with the C and D type parameters, they are for context-based script support.)
Then I made top level DSL functions to support syntax:
#PlsDsl
fun <T, C : ScriptContextBase, D : ScriptContextDescriptor<C>> static(block: () -> T): DynamicValue<T, C, D>
= DynamicValue<T, C, D>(value = block.invoke())
#PlsDsl
fun <T, C : ScriptContextBase, D : ScriptContextDescriptor<C>> dynamic(s: String): DynamicValue<T, C, D>
= DynamicValue<T, C, D>(script = s)
#PlsDsl
fun <T, C : ScriptContextBase, D : ScriptContextDescriptor<C>> dynamic(block: C.() -> T): DynamicValue<T, C, D> {
throw IllegalStateException("Can't use this format")
}
An explanation to the third form. As I wrote before, I don't want to execute the block of the function. When the script is executed, this form is converted to the string form, so normally this function would never appear in the script when executed. The exception is a sanity warning, which would never be thrown.
Finally added the field to my business object builder:
#PlsDsl
class MyObjectBuilder {
var value: DynamicValue<Int, SpecialScriptContext, SpecialScriptContextDescriptor>? = null
}
Second approach
The previous solution worked but had some flaws: the expression was not associated with the variable it set, neither with the entity the value was set in. With my second approach I solved this problem and removed the need of equal sign and most of the unnecessary curly brackets.
What helped: extension functions, infix functions and sealed classes.
First, I split the two value types into separated classes defined a common ancestor:
sealed class Value<T, C : ScriptContextBase> {
abstract val scriptExecutor: ScriptExecutor
abstract val descriptor: ScriptContextDescriptor<C>
abstract val code: String
abstract fun get(context: C): T?
}
class StaticValue<T, C : ScriptContextBase>(override val code: String,
override val scriptExecutor: ScriptExecutor,
override val descriptor: ScriptContextDescriptor<C>,
val value: T? = null
) : Value<T, C>() {
override fun get(context: C) = value
constructor(oldValue: Value<T, C>, value: T?) : this(oldValue.code, oldValue.scriptExecutor, oldValue.descriptor, value)
}
class DynamicValue<T, C : ScriptContextBase>(override val code: String,
script: String,
override val scriptExecutor: ScriptExecutor,
override val descriptor: ScriptContextDescriptor<C>)
: Value<T, C>() {
constructor(oldValue: Value<T, C>, script: String) : this(oldValue.code, script, oldValue.scriptExecutor, oldValue.descriptor)
private val scriptCache = scriptExecutor.register(descriptor)
val source = script?.replace("\\\"\\\"\\\"", "\"\"\"")
private val compiledScript = scriptCache.register(generateUniqueId(code), source)
override fun get(context: C): T? = compiledScript.execute<T?>(context)
}
Note, that I made the primary constructor internal and created a kind of copy and alter constructor. Then I defined the new functions as extension of the common ancestor and marked them infix:
infix fun <T, C : ScriptContextBase> Value<T, C>.static(value: T?): Value<T, C> = StaticValue(this, value)
infix fun <T, C : ScriptContextBase> Value<T, C>.expr(script: String): Value<T, C> = DynamicValue(this, script)
infix fun <T, C : ScriptContextBase> Value<T, C>.dynamic(block: C.() -> T): Value<T, C> {
throw IllegalStateException("Can't use this format")
}
Using the secondary copy-and-alter constructor allows to inherit the context sensitive values. Finally I initialize the value inside the DSL builder:
#PlsDsl
class MyDslBuilder {
var value: Value<Int, SpecialScriptContext> = StaticValue("pl.value", scriptExecutor, SpecialScriptContextDescriptor)
var value2: Value<Int, SpecialScriptContext> = StaticValue("pl.value2", scriptExecutor, SpecialScriptContextDescriptor)
}
Everything is in place and now I can use it in my script:
myobject {
value static 42
value2 expr "6 * 7"
value2 dynamic { calc(x, y) }
}

How do I generate a constructor parameter of the Kotlin "Unit" type with a single type parameter with kotlinpoet?

This might be a bit too specific for posting here, but I'm trying to generate a class like this with kotlinpoet:
class Query<out E: Model>(val onSuccess: (E) -> Unit, val onError: (Int, String) -> Unit = { i, m -> })
How would I create that type/constructor parameter with kotlinpoet? The docs do have the "Unit" type listed along with primitive types, so it seems to be a special case.
Here's a program producing the output you need:
class Model
fun main(args: Array<String>) {
val onSuccessType = LambdaTypeName.get(
parameters = TypeVariableName(name = "E"),
returnType = Unit::class.asTypeName())
val onErrorType = LambdaTypeName.get(
parameters = listOf(Int::class.asTypeName(), String::class.asTypeName()),
returnType = Unit::class.asTypeName())
val primaryConstructor = FunSpec.constructorBuilder()
.addParameter(ParameterSpec.builder(name = "onSuccess", type = onSuccessType)
.build())
.addParameter(ParameterSpec.builder(name = "onError", type = onErrorType)
.defaultValue("{ i, m -> }")
.build())
.build()
val querySpec = TypeSpec.classBuilder("Query")
.addTypeVariable(TypeVariableName(name = "out E", bounds = Model::class))
.addProperty(PropertySpec.builder(name = "onSuccess", type = onSuccessType)
.initializer("onSuccess")
.build())
.addProperty(PropertySpec.builder(name = "onError", type = onErrorType)
.initializer("onError")
.build())
.primaryConstructor(primaryConstructor)
.build()
val file = KotlinFile.builder(packageName = "", fileName = "test")
.addType(querySpec)
.build()
file.writeTo(System.out)
}
This prints (excluding generated imports) the following:
class Query<out E : Model>(val onSuccess: (E) -> Unit,
val onError: (Int, String) -> Unit = { i, m -> })
I'm hacking TypeVariableName here as out E, since there seems to be no better solution at the time. I'm also using the 0.4.0-SNAPSHOT version.
Easily enough, it's done by using the LambdaTypeName class. It was a bit confusing after getting used to kotlin's functional type style as opposed to java strictly functional interfaces. Here's what I used:
val typeVariable = TypeVariableName.invoke("E")
.withBounds(QueryData::class)
ParameterSpec.builder("onSuccess",
LambdaTypeName.get(null, listOf(typeVariable), UNIT)).build()
Which generates (with the class builders, of course):
class Query<E : QueryData> {
constructor(onSuccess: (E) -> Unit) {
}
}