Add a side-effect to a function in a generic way - kotlin

How can I write a Kotlin generic function that takes a function as an argument and adds a side-effect to it? For instance,
fun something(one: Int, two: String): String { return "${one}, ${two}" }
fun somethingElse(arg: Array<String>): String { return "${arg}" }
val w1 = wrapped(::something)
w1(42, "hello")
val w2 = wrapped(::somethingElse)
w2(arrayOf("ichi", "ni"))
The following works for functions that take only a single parameter:
fun <A, R> wrapped(theFun: (a: A) -> R): (a: A) -> R {
return { a: A ->
theFun(a).also { println("wrapped: result is $it") }
}
}
To make this work with an arbitrary number of arguments, I'd need some construct that gives me the type of the argument list. Unfortunately, the Function generic can't be used since it takes only one parameter. The following does not compile:
fun <A, R> wrapped(theFun: Function<A, R>): Function<A, R> {
return { args: A ->
theFun(*args).also { println("wrapped: result is ${it}") }
}
}
Or maybe I could use varargs? Does not seem to work with lambdas. Or Kotlin reflection?

Solution using reflection:
class KFunctionWithSideEffect<R>(private val f: KFunction<R>, private val sideEffect: (R) -> Unit) : KFunction<R> by f {
override fun call(vararg args: Any?) = f.call(*args).also { sideEffect(it) }
override fun callBy(args: Map<KParameter, Any?>) = f.callBy(args).also { sideEffect(it) }
}
fun <R> wrapped(theFun: KFunction<R>, sideEffect: (R) -> Unit = { str -> println("wrapped: result is $str") }) =
KFunctionWithSideEffect(theFun, sideEffect)
Usage:
val w1 = wrapped(::something)
w1.call(42, "hello")
val w2 = wrapped(::somethingElse)
w2.call(arrayOf("ichi", "ni"))

Related

How to use Either monad and avoid nested flatMap

I'm in a situation where I'm trying to setup some data and then call a service. Each step can fail, so I'm trying to use Arrow's Either to manage this.
But I'm ending up with a lot of nested flatMaps.
The following code snippet illustrates what I'm trying to do:
import arrow.core.Either
import arrow.core.flatMap
typealias ErrorResponse = String
typealias SuccessResponse = String
data class Foo(val userId: Int, val orderId: Int, val otherField: String)
data class User(val userId: Int, val username: String)
data class Order(val orderId: Int, val otherField: String)
interface MyService {
fun doSomething(foo: Foo, user: User, order: Order): Either<ErrorResponse, SuccessResponse> {
return Either.Right("ok")
}
}
fun parseJson(raw: String): Either<ErrorResponse, Foo> = TODO()
fun lookupUser(userId: Int): Either<ErrorResponse, User> = TODO()
fun lookupOrder(orderId: Int): Either<ErrorResponse, Order> = TODO()
fun start(rawData: String, myService: MyService): Either<ErrorResponse, SuccessResponse> {
val foo = parseJson(rawData)
val user = foo.flatMap {
lookupUser(it.userId)
}
//I want to lookupOrder only when foo and lookupUser are successful
val order = user.flatMap {
foo.flatMap { lookupOrder(it.orderId) }
}
//Only when all 3 are successful, call the service
return foo.flatMap { f ->
user.flatMap { u ->
order.flatMap { o ->
myService.doSomething(f, u, o)
}
}
}
}
I'm sure there is a better way to do this. Can someone help me with an idiomatic approach?
You can use the either { } DSL, this is available in a suspend manner or in a non-suspend manner through the either.eager { } builder.
That way you can use suspend fun <E, A> Either<E, A>.bind(): A.
Rewriting your code example:
fun start(rawData: String, myService: MyService): Either<ErrorResponse, SuccessResponse> =
either.eager {
val foo = parseJson(rawData).bind()
val user = lookupUser(foo.userId).bind()
val order = lookupOrder(foo.orderId).bind()
myService.doSomething(foo, user, order).bind()
}
If you run into an Either.Left, then bind() will short-circuit the either.eager block and return with the encountered Either.Left value.

How to change my helper function so that is collects the results of the parallel processing tasks

I wrote this helper function, so that I can easily process a list in parallel and only continue code execution when all the work is done. It works nicely when you don't need to return a result.
(I know it isn't the best practice to create new pools every time, it can be easily moved out, but I wanted to keep the examples simple.)
fun recursiveAction(action: () -> Unit): RecursiveAction {
return object : RecursiveAction() {
override fun compute() {
action()
}
}
}
fun <T> List<T>.parallelForEach(parallelSize: Int, action: (T) -> Unit) {
ForkJoinPool(parallelSize).invoke(recursiveAction {
this.parallelStream().forEach { action(it) }
})
}
Example use:
val myList: List<SomeClass> [...]
val parallelSize: Int = 8
myList.parallelForEach(parallelSize) { listElement ->
//Some task here
}
Is there any way to make a similar helper construct for when you want to collect the results back into a list?
I know I have to use a RecursiveTask instead of the RecursiveAction, but I couldn't manage to write a helper function like I had above to wrap it.
I'd like to use it like this:
val myList: List<SomeClass> [...]
val parallelSize: Int = 8
val result: List<SomeClass> = myList.parallelForEach(parallelSize) { listElement ->
//Some task here
}
Alternatively, is there a simpler way to do this alltogether?
Answered by JeffMurdock over on Reddit
fun <T> recursiveTask(action: () -> T): RecursiveTask<T> {
return object : RecursiveTask<T>() {
override fun compute(): T {
return action()
}
}
}
fun <T, E> List<T>.parallelForEach(parallelSize: Int, action: (T) -> E): List<E> {
val pool = ForkJoinPool(parallelSize)
val result = mutableListOf<ForkJoinTask<E>>()
for (item in this) {
result.add(pool.submit(recursiveTask {
action(item)
}))
}
return result.map { it.join() }
}
fun main(args: Array<String>) {
val list = listOf(1, 2, 3)
list.parallelForEach(3) { it + 2 }.forEach { println(it) }
}

Reduce this to a simple lambda

I would like to simplify this code by reducing it to just a lambda. The interface has only one function. I'm not sure how to replace the override part of the code with just a lambda expression:
interface ITextWatcher {
fun onTextChanged(text: String) {
}
}
val textChangeHandler = object: ITextWatcher {
override fun onTextChanged(text: String)
var t = text
}
}
I'm looking for something like this:
val textChangeHandler = object: ITextWatcher {text ->
}
But that won't compile.
The syntax is val textChangeHandler = ITextWatcher {text -> ... }, but it doesn't work for interfaces declared in Kotlin, only for Java ones (at least for now).
Use (String) -> Unit directly instead. Or declare a function to convert one to another:
inline fun ITextWatcher(crossinline f: (String) -> Unit) = object : ITextWatcher {
override fun onTextChanged(text: String) {
f(text)
}
}
val textChangeHandler = ITextWatcher {text -> ... }
if you want.

How to pass is operator parameter through function in Kotlin

val k = " asdfasdf "
fun test() {
if(k is String) {
// Do something
}
}
So, how do I pass that String through the function calls
eg:
fun test(xxxx) {
if(k is xxxx) {
// do something
}
}
Like this:
inline fun <reified T> testType(k: Any) {
if(k is T) {
println("is a ${T::class.simpleName}")
} else {
println("is not a ${T::class.simpleName}")
}
}
Call it like this:
test<String>("Hello") // is a String
test<String>(1) // is no String
Here some further reading.
There are two possibilities, depending on your needs.
1. Use inline and a reified type parameter
You can use the reified keyword on the type parameter in combination with an inline function:
inline fun <reified T> test(k: Any) {
if (k is T) {
println("k is a T!")
}
}
See the documentation on reified.
2. Use KClass<T>
If you do not want to or cannot make your function inline you can use a KClass parameter:
fun <T : Any> test(k: Any, type: KClass<T>) {
if (type.isInstance(k)) {
println("k is a T!")
}
}
You can either use a predicate, e.g.:
fun testIt(predicate: (Any?) -> Boolean) {
if (predicate(k)) {
println("matches!")
} else println("nope")
}
and call it as follows:
testIt { it is String }
testIt { it is Int }
Or you can use a reified type:
inline fun <reified T> testIt() {
when (k) {
is T -> println("matches!")
else -> println("nope")
}
}
and call it like:
testIt<String>()
testIt<Int>()
For simplicity I kept your current variable inside the testIt-method... you may want to redesign that ;-)
I basically assumed a member variable as follows: var k : Any? = null
inline fun <reified T> isType(obj: Any): Boolean {
return obj is T
}
fun main(args: Array<String>) {
val test = "This is a String"
if (isType<String>(test)) {
println("Success")
} else {
println("Failure")
}
}

Can't reify a suspendable block passed to forEach

Given some
suspend fun a(): Int
This works:
launch(Unconfined) {
(1..10).forEach {
val a = a()
println("Result is $a")
}
}
But this fails at compile time:
val action: (Int) -> Unit = {
// Suspend function should be called only from a coroutine
// or another suspend function:
val a = a()
println("Result is $a")
}
launch(Unconfined) {
(1..10).forEach(action)
}
Furthermore, it isn't fixable because:
val action: suspend (Int) -> Unit = {
val a = a()
println("Result is $a")
}
launch(Unconfined) {
// suspend (Int) -> Unit cannot be applied to (T) -> Unit
(1..10).forEach(action)
}
What is the story here in terms of the static type system? The current situation looks like a quick hack where an inline block containing a suspend fun call is still inferred to a non-suspend type signature.
Is this an area where the design will be improved before being finalized?
The suspend and normal functional types are not subtypes of each other and thus cannot be assigned or passed to a function in place of each other:
val f: () -> Unit = { }
val g: suspend () -> Unit = f // Type mismatch
val f: suspend () -> Unit = { }
val g: () -> Unit = f // Type mismatch
This is why a suspend (Int) -> Unit cannot be passed to forEach.
Basically, the restriction for suspend functions to be called only in other suspend functions works irrespective to the type system. Such calls should simply be placed inside a suspend function or a suspend lambda or inlined into one. So, this should also work:
val action: suspend (Int) -> Unit = {
val a = a()
println("Result is $a")
}
launch(Unconfined) {
(1..10).forEach { action() } // The call is inlined into a suspend lambda
}
I've filed an issue about supporting (1..10).forEach(action) as well: KT-22186