Kotlin compose list of functions - kotlin

Currently I am using compose from a library called arrow which has it defined this way.
inline infix fun <IP, R, P1> ((IP) -> R).compose(crossinline f: (P1) -> IP): (P1) -> R = { p1: P1 -> this(f(p1)) }
What I am trying to do is compose functions from a list so I assumed something as simple as this would work.
val add5 = { i: Int -> Option(i + 5) }
val multiplyBy2 = { i: Int -> i * 2 }
fun isOdd(x: Option<Int>) = x.map { y -> y % 2 != 0 }
val composed = listOf(::isOdd, add5, multiplyBy2).reduce { a, b -> a compose b }
but I get type error:
Type inference failed: Cannot infer type parameter IP in inline infix
fun ((IP) -> R).compose(crossinline f: (P1) -> IP): (P1)
-> R None of the following substitutions receiver: (Any) -> Any arguments: ((Nothing) -> Any) receiver: (Nothing) -> Any arguments:
((Nothing) -> Nothing) can be applied to receiver: Function1<, Any>
arguments: (Function1<, Any>)
so I try:
val composed = listOf<(Any) -> Any>(::isOdd, add5, multiplyBy2).reduce { x, y -> x compose y }
and I get this:
Type mismatch: inferred type is KFunction1<#ParameterName Option, Option> but (Any) -> Any was expected
Type mismatch: inferred type is (Int) -> Option but (Any) -> Any was expected
Type mismatch: inferred type is (Int) -> Int but (Any) -> Any was expected
Any help appreciated. I don't mind if I end up having to write my own version of compose. I just need to be able to compose a list of functions.
edit:
This works no problems:
val composed = ::isOdd compose add5 compose multiplyBy2
I am just trying to achieve the same result should I have a list of functions instead of writing this way.

I find it hard to imagine how a simple compose should work with methods having so different signatures. So first we would have to align the types of the functions. Arrow let's you compose functions if the return type of the first matches the parameter of the second...
Another problem is that isOdd is a Predicate. It is not transforming a value.
If the transformers have a compatible signature you can compose them with e.g. andThen
Here is a version that aligns the types to compose the functions. Note that filter and map are special functions in arrow's Option that allow you to pass transformer functions/predicates
import arrow.core.Option
import arrow.core.andThen
import org.hamcrest.Matchers.`is`
import org.junit.Assert.assertThat
import org.junit.Test
class ComposeTest {
#Test
fun shouldCompose() {
val add5 = { i: Int -> i + 5 }
val multiplyBy2 = { i: Int -> i * 2 }
val isOdd = { x: Int -> x % 2 != 0 }
val composed: (Int) -> Option<Int> = { i: Int -> Option.just(i)
.filter(isOdd)
.map(add5.andThen(multiplyBy2))
}
assertThat(composed(3), `is`(Option.just(16)))
assertThat(composed(4), `is`(Option.empty()))
}
}

Related

Compose blocks with different dispatch receivers

Setup:
interface AProvider {
fun getA(): String
}
interface BProvider {
fun getB(): String
}
fun a(block: AProvider.() -> Unit) {}
fun b(block: BProvider.() -> Unit) {}
With this, I can nest the two functions as follows
val x = a { b { getA(); getB() } }
Now I would like to abstract this particular pattern to a higher level function so that I can pass the inner block and call both getA() and getB() in it, e.g. something like this:
val y = { l: ??? -> a { b(l) } }
val z = y { getA(); getB() }
The questions are
Is this possible with the proposed definition of y?
If yes, what should be the type of l?
If not, is there some other definition where I can get access to multiple dispatch receivers inside a lambda?
Note: This is related to my other SO question
You can do this with context receivers, which allows you to specify multiple receivers for one lambda. The type of y would be:
(context(AProvider, BProvider) () -> Unit) -> Unit
That is, a function that takes another function as a parameter, and returns Unit. The function parameter that it takes also returns Unit, but has AProvider and BProvider as its context receivers.
val y: (context(AProvider, BProvider) () -> Unit) -> Unit = { l ->
a { b { l(this#a, this#b) } }
}
val z = y { getA(); getB() }
Notice that when we call l, we pass the context receivers, this#a and this#b, as if they are regular parameters.
This makes z a Unit as well, which is kind of weird. y returns whatever a returns after all, so perhaps you did not intend a to return Unit.

Kotlin - Infer type for one of two generic parameters

I am trying to create a function that has two generic types: one reified, and another derived from the context of its usage (since it is an extension function):
inline fun <reified E, A> Either<Throwable, A>.bypassLeft(transformation: Throwable.() -> A): Either<Throwable, A> =
when (this) {
is Either.Left -> when (value) {
is E -> value.transformation().right()
else -> this
}
else -> this
}
The idea would be to call the function just mentioning the reified type, something like:
a.bypassLeft<NoResultException> { "" }
In which "a" is an object of type Either<Throwable,String>
But the compiler is not letting me go away with it, and requires me to specify both generic types, instead of deriving the second one form the object calling the function.
It seemed quite a reasonable thing to be possible, but maybe I am wrong...
Is this possible to achieve? If so, what am I doing wrong?
It's not currently possible with a function to ascribe a single type argument and leave the other inferred. You can achieve what you want if you type the lambda arguments by changing your implementation to not use a receiver type.
I threw in there an additional impl that shows how type args can also be partially applied with a class or other surrounding scope.
import arrow.core.Either
import arrow.core.right
inline fun <reified E : Throwable, A> Either<Throwable, A>.bypassLeft(
transformation: (E) -> A //changed to regular arg not receiver
): Either<Throwable, A> =
when (this) {
is Either.Left -> when (val v = value) { //name locally for smart cast
is E -> transformation(v).right()
else -> this
}
else -> this
}
class Catch<A>(val f: () -> A) { //alternative impl with partial type app
inline fun <reified E : Throwable> recover(
recover: (E) -> A
): Either<Throwable, A> =
Either.catch(f).fold(
{
if (it is E) Either.Right(recover(it))
else Either.Left(it)
},
{
Either.Right(it)
}
)
}
suspend fun main() {
val x: Either<Throwable, Int> = Either.Left(StackOverflowError())
val recovered = x.bypassLeft {
s: StackOverflowError -> //here infers E
0 // here infers A
}
println(recovered) // Either.Right(0)
val notRecovered: Either<Throwable, Int> =
Catch {
throw NumberFormatException()
1
}.recover<StackOverflowError> { 0 }
println(notRecovered) // Either.Left(java.lang.NumberFormatException)
}
This is possible as of Kotlin v1.7.0 with the underscore operator.
The underscore operator _ can be used for type arguments. Use it to automatically infer a type of the argument when other types are explicitly specified:
interface Foo<T>
fun <T, F : Foo<T>> bar() {}
fun baz() {
bar<_, Foo<String>>() // T = String is inferred
}
In your example, it would be possible like this:
a.bypassLeft<NoResultException, _> { "" }

ArrowKT - Lens invoke giving compilation error

I'm trying to follow this documentation about Lens in Arrow. However when I try to write a Lens
data class Player(val health: Int)
val playerLens: Lens<Player, Int> = Lens(
get = { player -> player.health },
set = { player, value -> player.copy(health = value) }
)
I get the following error:
4 type arguments expected for operator fun <S, T, A, B> invoke(): PLens<S, T, A, B>
I see that Lens<S, T> is a typealias for PLens<S, S, T, T>, so why this compilation error?
Kotlin version 1.3.50
Arrow Optics version 0.10.0
Kotlin typealias doesn't work here as expected. When calling constructor, you need to specify 4 params here:
Lens<Player, Player, String, String>(
get = { v -> v.health },
set = { v, value -> player.copy(health = value) }
)
Our don't specify them at all, compiler could infer them for you from lambda parameters types
Lens(
get = { player: Player -> player.health },
set = { player: Player, value: Int -> player.copy(health = value) }
)

How to get around Type mismatch Required: Foo<Type>, Found: Foo<Type?>

Given the following Kotlin code:
class Foo<T>(val t : T?)
fun <T : Any, R : Any> Foo<T?>.transform(transformer : (T) -> R) : Foo<R?> {
return when (t) {
null -> Foo(null)
else -> Foo(transformer(t))
}
}
fun main(args : Array<String>) {
val foo = Foo(args.firstOrNull())
val bar = foo.transform<String, Int> { t -> t.length }
val baz = bar.transform<Int, IntRange> { t -> t..(t + 1) }
}
Why do I get the following error:
Type mismatch. Required: Foo<String?> Found: Foo<String>
If I remove the ? from the extension function to be Foo<T>.transform I instead get the following error:
Type mismatch. Required: Foo<Int> Found: Foo<Int?>
I can understand the second error, because you cannot assign Int? to Int, but the first doesn't make any sense, as you can assign String to String?
EDIT:
I have modified the class Foo<T> to be class Foo<out T> and this works for me as the value t will only ever be read after the initial assignment. With this option I do not need to define the type parameters at the call site of transform.
Another option I have found that I think is a bit messy (and not sure why it makes a difference) is adding a third type parameter to the extension function as follows:
fun <T : Any, U : T?, R : Any> Foo<U>.transform(transformer : (T) -> R) : Foo<R?>
The call site of this on the other hand I find a bit odd. Looking at the above code the call of foo.transform MUST NOT include the type parameters, but the call of bar.transform<Int, Int?, IntRange> MUST include the type parameters in order to work.
This option allows setting the value t at some later point if it were a var instead of val. But it also removes the smart casting on t in the transform function. Although that can be gotten around with a !! if you are not worried about race conditions or (with some additional effort) ?: or ?. if you are worried about race conditions.
You can change your Foo<T> class to be not invariant (see https://kotlinlang.org/docs/reference/generics.html):
class Foo<out T>(val t : T?)
fun <T : Any, R : Any> Foo<T?>.transform(transformer : (T) -> R) : Foo<R?> {
return when (t) {
null -> Foo(null)
else -> Foo(transformer(t))
}
}
fun main(args : Array<String>) {
val foo = Foo(args.firstOrNull())
val bar = foo.transform<String, Int> { t -> t.length }
val baz = bar.transform<Int, IntRange> { t -> t..(t + 1) }
}
The out T specifies precisely the behavior you want.
Since you specify the property t in the constructor as T? you don't need to specify Foo<T?> as receiver and Foo<R?> as return type. Instead use Foo<T> and Foo<R> and it will work.
class Foo<T>(val t : T?)
fun <T: Any, R: Any> Foo<T>.transform(transformer : (T) -> R) : Foo<R> {
return when (t) {
null -> Foo(null)
else -> Foo(transformer(t))
}
}
fun main(args : Array<String>) {
val foo = Foo(args.firstOrNull())
val bar = foo.transform { t -> t.length }
val baz = bar.transform { t -> t..(t + 1) }
}
Note: You don't need to specify the generic types for transform because they can be inferred (at least in this example).

Kotlin - overload function with lambda arguments

Suppose I want to have a function that checks whether a property value differs in two given objects. If so, a handler lambda should be executed.
fun <P>onChange(oldObj: P, newObj: P, select: (P) -> Any, handler: (P) -> Unit) {
val newValues = select(newObj)
val oldValues = select(oldObj)
if (oldValues != newValues) { handler(newObj) }
}
It works totally fine for the following use case:
data class TestObj(
val foo: String,
val bar: String? = null
)
val oldObj = TestObj(foo = "foo")
val newObj = TestObj(foo = "foo1", bar = "bar")
onChange(oldObj, newObj, { it.foo }) {
print("foo did change: ${it.foo}")
}
Now I want to be able to check if bar has changed.
If I try to to overload the function like
fun <P>onChange(oldObj: P, newObj: P, select: (P) -> Any?, handler: (P) -> Unit) {
...
}
the compiler throws an Duplicate method name&signature exception.
How do I overload the function or modify the signature in order to support a select lambda that is able to return an optional?
(P) -> Any? is a subtype of (P) -> Any, so if you just have
fun <P>onChange(oldObj: P, newObj: P, select: (P) -> Any?, handler: (P) -> Unit)
you can pass a (P) -> Any there. Of course, with overloading you can have two different implementations, but if behavior is actually different, this seems like a very bad idea :)
Change the name. They have the same JVM signature because nullable types don't exist at the byte code level. They are a Kotlin feature enforced at compile time.