Receive transformation map function to send into `List<>.mapNotNull()` - kotlin

Im trying to write a function like transform that receives a function that will be used inside of mapNotNull but I cant find a way to do it.
Example
val items: List<String?> = listOf(null, "cosa")
fun transform(transformer: (String) -> String?) {
items.mapNotNull(transformer) // <-------------------------------------- THIS DOES NOT COMPILE
}
fun main() {
val items: List<String?> = listOf(null, "cosa")
val transformer: (String) -> String? = {
null
}
val map = transform(transformer)
print(map)
}
You can check how this works here: play.kotlinlang
How can I declare the parameter of fun transform to be able to pass it inside of the mapNotNull ?

The mapNotNull function is defined as:
public inline fun <T, R : Any> Iterable<T>.mapNotNull(transform: (T) -> R?): List<R>
in other words, the type of the parameter to the transform lambda is T, where T is the type of the Iterable being operated on. In your case, your iterable is a List of type String?.
Therefore, you need to declare your transformer as type (String?) -> String?, and only the non-null results of that transform will be included in the result.
To update the code you supplied on play.kotlinlang, with a few additional modifications to make the type declarations a bit more idiomatic -- note, I've left the code mostly as-is, despite the odd use of the additional transform function:
val items = listOf<String?>(null, "cosa")
fun transform (transformer: (String?) -> String?): List<String> {
return items.mapNotNull(transformer)
}
fun main() {
val items = listOf<String?>(null, "cosa")
val transformer: (String?) -> String? = {
// this of course means the output of transform will always be empty
null
}
val map = transform(transformer)
print(map)
}

You have a list of nullable strings.
mapNotNull applies the transform function to an each element in a list and then checks if the result of this functions is null. So in this case, it passes a nullable string in the transformer function, and that function definitely cannot be of (String) -> String? type because the parameter here is a non-nullable string.
You should either declare the transformer function as (String?) -> String?, or remove nulls from list before calling mapNotNull:
items.filterNotNull().mapNotNull(transformer)
Another option is to wrap transformer into a lambda function before passing it to mapNotNull and handle null elements there, for example:
items.mapNotNull { e -> e?.let(transformer) }
this applies transformer function to an element only if it is not null.

Related

Map return type from input generic type in Kotlin

I have a function that returns IMyInterface
fun getValue(type: Types): IMyInterface? {}
But I have to always cast the return type in this way before I can use it:
getValue(Types.TypeInt)?.let { value ->
val usableVale = MyInterfaceAsInt.cast(value)
// more code...
}
MyInterfaceAsInt implements IMyInterface and I have no control over them.
The casting always depend of the input, so
Types.TypeInt -> MyInterfaceAsInt.cast(value)
Types.TypeLong -> MyInterfaceAsLong.cast(value)
...etc
Is there a way to define somthing like fun <T = Types> getValue(type: T) in a way that the return type can be inferred from type ?
I would like to do the casting inside getValue.
It looks like Types.TypesInt/Long/etc. are simply instances of the same type Types, not different types; and in fun <T> getValue(type: T), T has to be a type. So it doesn't seem to be possible.
But I would probably go the other way and define functions like
fun getValueAsInt(): MyInterfaceAsInt? = getValue(Types.TypeInt)?.let { MyInterfaceAsInt.cast(it) }
fun getValueAsLong(): MyInterfaceAsLong? = getValue(Types.TypeLong)?.let { MyInterfaceAsLong.cast(it) }
...
Another alternative which could be useful at least when the type can be inferred:
#Suppress("UNCHECKED_CAST")
inline fun <reified T : MyInterface> getValue(): T? = when(T::class) {
MyInterfaceAsInt::class -> getValue(Types.TypeInt)?.let { MyInterfaceAsInt.cast(it) }
MyInterfaceAsLong::class -> getValue(Types.TypeLong)?.let { MyInterfaceAsLong.cast(it) }
...
} as T

How to use reduce function correctly with functional programming in kotlin

I am trying to pass a function and want to use reduce on it.
This is the data class
data class TestClass(
var variable1: BigDecimal?,
var variable2: BigDecimal?
)
This is the function which I want to work
fun aggregateAll() {
helperfunc(TestClass::variable1::get,someDummmyFilledListOfTestClass)
}
fun helperfunc(function: () ->BigDecimal, testList:List<TestClass>) {
var aggregatedValue:BigDecimal = BigDecimal.ZERO
testList.map{function}.reduce{aggregatedValue,element -> aggregatedValue.add(element)}
}
fun main() {
val someDummmyFilledListOfTestClass = listOf<TestClass>(
TestClass(1.toBigDecimal()),
TestClass(199.toBigDecimal()),
TestClass(null),
TestClass(501.toBigDecimal())
)
val result = helperfunc(someDummmyFilledListOfTestClass, TestClass::variable1::get)
// trailing lambda
// or val result = helperfunc(someDummmyFilledListOfTestClass) { it.variable1 }
println(result)
}
/* lambda last parameter for trailing lambda */
fun helperfunc(testList: List<TestClass>, function: (TestClass) -> BigDecimal?): BigDecimal {
return testList
// map TestClass to BigDecimal? and filter nulls
.mapNotNull(function)
// fold with initial value, reduce will throw exception if list is empty
.fold(BigDecimal.ZERO) { acc, element -> acc.add(element) }
}
data class TestClass(
var variable1: BigDecimal?
// var variable2: BigDecimal?
)
For the map function to work, your function type needs a TestClass parameter, so it should be (TestClass) -> BigDecimal.
And your variable1 needs to be non-nullable, or it else its getter is a (TestClass) -> BigDecimal?, which doesn't match the required type.
And when you pass the function to map, don't wrap it in a lambda, because then you are just creating a function that returns your other function.
And you seem to want to use a separate aggregator rather than the first element of the list, so that means you should use fold instead of reduce.
data class TestClass(
var variable1: BigDecimal,
var variable2: BigDecimal?
)
fun aggregateAll() {
helperfunc(TestClass::variable1::get, someDummmyFilledListOfTestClass)
}
fun helperfunc(function: (TestClass) -> BigDecimal, testList:List<TestClass>) {
val sum: List<BigDecimal> =
testList.map(function).fold(BigDecimal.ZERO) { aggregatedValue, element -> aggregatedValue.add(element)}
}

Unexpected behavior of also/apply, cannot pass references of a instance function into also/apply

To sum up the question in a few words, here is the catch:
The also(strings::add) doesn't work, it says Type inference failed
fun test() = "Test String"
val strings = mutableListOf<String>()
// Type inference failed: inline fun <T> T.also(block: (T) -> Unit): T cannot be applied to receiver: String arguments: (<unknown>)
// None of the following functions can be called with the arguments supplied: public abstract fun add(index: Int, element: String): Unit defined in kotlin.collections.MutableList public abstract fun add(element: String): Boolean defined in kotlin.collections.MutableList
test().also(strings::add).let { /* Use the string later */ }
While doing the same with let does work in the same place:
val strings = mutableListOf<String>()
test().let(strings::add).let { println(it) } // prints true, no compile errors.
Here is the minimal reproducable code.
I want to use the string later so don't want to use let here. What should I do? If i try to use the apply the same compile error occur probably because both also and apply have same callback signature of KFunction1<T, T>. How should one pass these type of references with also/apply?
override fun add(element: E): Boolean as you can see, the function returns Boolean, but apply accepts block: T.() -> Unit, i.e. it accepts only functions that receive a single argument and return no value.

Kotlin: How to specify a named arguent with a variable?

Suppose I have two methods:
private fun method1(a: A): A {
return a.copy(v1 = null)
}
private fun method2(a: A): A {
return a.copy(v2 = null)
}
Can I write something like:
private fun commonMethod(a: A, variableToChange: String): A {
return a.copy($variableToChange = null)
}
Another words, can I use a variable to refer to a named argument?
If I understand correctly what you are trying to archive I would recommend to pass a setter to the method e.g.
fun <A> changer (a: A, setter: (a: A) -> Unit ) {
// do stuff
setter(a)
}
Is this what you are looking for?
A possible solution for this problem (with usage of reflection) is:
inline fun <reified T : Any> copyValues(a: T, values: Map<String, Any?>): T {
val function = a::class.functions.first { it.name == "copy" }
val parameters = function.parameters
return function.callBy(
values.map { (parameterName, value) ->
parameters.first { it.name == parameterName } to value
}.toMap() + (parameters.first() to a)
) as T
}
This works with all data classes and all classes that have a custom copy function with the same semantics (as long as the parameter names are not erased while compiling). In the first step the function reference of the copy method is searched (KFunction<*>). This object has two importent properties. The parameters property and the callBy function.
With the callBy function you can execute all function references with a map for the parameters. This map must contain a reference to the receiver object.
The parameters propery contains a collection of KProperty. They are needed as keys for the callBy map. The name can be used to find the right KProperty. If a function as a parameter that is not given in the map it uses the default value if available or throws an exception.
Be aware that this solution requires the full reflection library and therefore only works with Kotlin-JVM. It also ignores typechecking for the parameters and can easily lead to runtime exceptions.
You can use it like:
data class Person (
val name: String,
val age: Int,
val foo: Boolean
)
fun main() {
var p = Person("Bob", 18, false)
println(p)
p = copyValues(p, mapOf(
"name" to "Max",
"age" to 35,
"foo" to true
))
println(p)
}
// Person(name=Name, age=15, foo=false)
// Person(name=Max, age=35, foo=true)

Kotlin: store any type of function reference in a variable

Is it possible to have a variable that can holds any type function.
Like :
fun method1(par: Boolean){}
fun method2(par: Boolean) : Int{return 1}
fun method3(par: Boolean, par2: Boolean) : Int{return 1}
var funtionHolder : ((Any)->Any) ?= null //What should I write here?? so to hold any type of function
fun method4(){
.........
funtionHolder = ::method1 //getting compile time error
.........
funtionHolder = ::method2 //getting compile time error
.........
funtionHolder = ::method3 //getting compile time error
}
After holding the function_reference I need to invoke it later. So I need to holds it parameter type and state also.
You can hold them in a KFunction<Any> or its superclass KCallable<Any> because you know nothing about the parameter list and nothing about the return type, so you have to go to something that can reference at that level of abstraction. These instances can then be invoked more generically using the call() or callBy() methods. (this requires the kotlin-reflect dependency). To do something safer and to call like a normal function you'd have to cast back to the specific function type later.
If you want to avoid this, you'll need to unify your signatures to something you can point to with another function type (i.e. KFunction1 or KFunction2). Otherwise how you'll call this, what you'll do with it will be up to you at this point because you erased all the information that allows you to easily call the function.
val functionHolder1: KFunction<Any> = ::method1 // success!
val functionHolder2: KFunction<Any> = ::method2 // success!
val functionHolder3: KFunction<Any> = ::method3 // success!
You can then make a DeferredFunction class to hold these along with parameters you want to later pass, and then invoke it whenever in the future.
class DeferredFunction(val function: KFunction<Any>, vararg val params: Any?) {
#Suppress("UNCHECKED_CAST")
operator fun <T> invoke(): T {
return function.call(params) as T
}
}
fun whatever(name: String, age: Int): String {
return "$name of age $age"
}
val functionHolder = DeferredFunction(::whatever, "Fred", 65)
println(functionHolder<String>()) // "Fred of age 65"
You do not need the generic return type on the invoke function and could just make it return Any or call it as functionHolder<Any>() but it is nice if you know what to expect for the return. You can decide what to do there based on your actual use case. Also no need to special case for no parameters, just don't pass any, i.e. DeferredFunction(::otherFunc)
With reference from Jayson's answer, added extra code to hold the state of the function by using vararg and spread operator(*).
var functionHolder: KFunction<Any> ?= null
var paramsHolder : Array<out Any?> ?= null
fun hold(functionReference : KFunction<Any>, vararg args : Any?) {
this.functionHolder = functionReference
this.paramsHolder = args
}
fun release() {
if (functionHolder != null) {
if (paramsHolder != null) {
functionHolder?.call(*paramsHolder!!)
} else {
functionHolder?.call()
}
}
}
......
fun method3(par: Boolean, par2: Boolean) : Int{return 1}
......
hold(::method3, true, false)
release()//it works
No. Kotlin is static typed language and doesn't allow this. Else what happens when this is called?
functionHolder->invoke(3)
and when functionHolder is assigned a lamda that doesn't take parameter?