How to use `when` with 2 sealed classes and getting the inner value? - kotlin

Consider this extreme simplified code (available on https://pl.kotl.in/bb2Irv8dD):
sealed class Person {
data class A(val i: Int) :
Person()
}
fun main() {
val a = Person.A(i = 0)
val b = Person.A(i = 1)
// Compiles
when (a) {
is Person.A -> print("I have access to {$a.i}")
}
// Does not compile :(
when (a to b) {
is Person.A to is Person.A -> print("I have access to {$a.i} and b {$b.i}")
}
}
Why does the (a to b) code not work? It works for 1 variable, I was hoping I can match on both classes and get both inner values.
The error is:
Incompatible types: Person.A and Pair<Person.A, Person.A> Expecting
'->' Expecting an element Incompatible types: Person.A and
Pair<Person.A, Person.A>

Aside from that syntax not being supported (you can only use is on one thing in a when branch), by using to you're literally creating an instance of the Pair class.
Pair uses generics for the types of its two variables, so this type information is lost at runtime due to type erasure.
So although, you can do this:
when (a to b) {
is Pair<Person.A, Person.A> -> print("I have access to {$a.i} and b {$b.i}")
}
it is only allowed when both a and b are local variables whose types are declared locally, so that the generic types of the Pair are known at compile time. But this makes it mostly useless, because if a and b are local variables with known type at compile time, then you could just replace the above with true or false.
To be able to do something like this in a general way, you must either create local variables to use:
val aIsTypeA = a is Person.A
val bIsTypeA = b is Person.A
when (aIsTypeA to bIsTypeA) {
true to true -> //...
//...
}
or use when without a subject and put the full condition on each branch:
when {
a is Person.A && b is Person.A -> //...
//...
}

The (a to b) returns a Pair<Person.A,Person.A> but what you are checking is Type Person.A to Type Person.A instead of the Type Pair<Person.A,Person.A>.
What you can do instead is:
when (a to b) {
is Pair<Person.A,Person.A> -> print("I have access to {$a.i} and b {$b.i}")
}

Related

Generic transpose (or anything else really!) in Kotlin

Working on an Advent of Code puzzle I had found myself defining a function to transpose matrices of integers:
fun transpose(xs: Array<Array<Int>>): Array<Array<Int>> {
val cols = xs[0].size // 3
val rows = xs.size // 2
var ys = Array(cols) { Array(rows) { 0 } }
for (i in 0..rows - 1) {
for (j in 0..cols - 1)
ys[j][i] = xs[i][j]
}
return ys
}
Turns out that in the following puzzle I also needed to transpose a matrix, but it wasn't a matrix of Ints, so i tried to generalize. In Haskell I would have had something of type
transpose :: [[a]] -> [[a]]
and to replicate that in Kotlin I tried the following:
fun transpose(xs: Array<Array<Any>>): Array<Array<Any>> {
val cols = xs[0].size
val rows = xs.size
var ys = Array(cols) { Array(rows) { Any() } } // maybe this is the problem?
for (i in 0..rows - 1) {
for (j in 0..cols - 1)
ys[j][i] = xs[i][j]
}
return ys
}
This seems ok but it isn't. In fact, when I try calling it on the original matrix of integers I get Type mismatch: inferred type is Array<Array<Int>> but Array<Array<Any>> was expected.
The thing is, I don't really understand this error message: I thought Any was a supertype of anything else?
Googling around I thought I understood that I should use some sort of type constraint syntax (sorry, not sure it's called like that in Kotlin), thus changing the type to fun <T: Any> transpose(xs: Array<Array<T>>): Array<Array<T>>, but then at the return line I get Type mismatch: inferred type is Array<Array<Any>> but Array<Array<T>> was expected
So my question is, how do I write a transpose matrix that works on any 2-dimensional array?
As you pointed out yourself, the line Array(cols) { Array(rows) { Any() } } creates an Array<Array<Any>>, so if you use it in your generic function, you won't be able to return it when Array<Array<T>> is expected.
Instead, you should make use of this lambda to directly provide the correct value for the correct index (instead of initializing to arbitrary values and replacing all of them):
inline fun <reified T> transpose(xs: Array<Array<T>>): Array<Array<T>> {
val cols = xs[0].size
val rows = xs.size
return Array(cols) { j ->
Array(rows) { i ->
xs[i][j]
}
}
}
I don't really understand this error message: I thought Any was a supertype of anything else?
This is because arrays in Kotlin are invariant in their element type. If you don't know about generic variance, it's about describing how the hierarchy of a generic type compares to the hierarchy of their type arguments.
For example, assume you have a type Foo<T>. Now, the fact that Int is a subtype of Any doesn't necessarily imply that Foo<Int> is a subtype of Foo<Any>. You can look up the jargon, but essentially you have 3 possibilities here:
We say that Foo is covariant in its type argument T if Foo<Int> is a subtype of Foo<Any> (Foo types "vary the same way" as T)
We say that Foo is contravariant in its type argument T if Foo<Int> is a supertype of Foo<Any> (Foo types "vary the opposite way" compared to T)
We say that Foo is invariant in its type argument T if none of the above can be said
Arrays in Kotlin are invariant. Kotlin's read-only List, however, is covariant in the type of its elements. This is why it's ok to assign a List<Int> to a variable of type List<Any> in Kotlin.

Understanding Validated.applicative in kotlin arrow library

I come across below generic function which takes two Either type and a function as an argument. If both arguments are Either.Right then apply the function over it and returns the result, if any of the argument is Either.Left it returns NonEmptyList(Either.Left). Basically it performs the independent operation and accumulates the errors.
fun <T, E, A, B> constructFromParts(a: Either<E, A>, b: Either<E, B>, fn: (Tuple2<A, B>) -> T): Either<Nel<E>, T> {
val va = Validated.fromEither(a).toValidatedNel()
val vb = Validated.fromEither(b).toValidatedNel()
return Validated.applicative<Nel<E>>(NonEmptyList.semigroup()).map(va, vb, fn).fix().toEither()
}
val error1:Either<String, Int> = "error 1".left()
val error2:Either<String, Int> = "error 2".left()
val valid:Either<Nel<String>, Int> = constructFromParts(
error1,
error2
){(a, b) -> a+b}
fun main() {
when(valid){
is Either.Right -> println(valid.b)
is Either.Left -> println(valid.a.all)
}
}
Above code prints
[error 1, error 2]
Inside the function, it converts Either to ValidatedNel type and accumulates both errors
( Invalid(e=NonEmptyList(all=[error 1])) Invalid(e=NonEmptyList(all=[error 2])) )
My question is how it performs this operation or could anyone explain the below line from the code.
return Validated.applicative<Nel<E>>(NonEmptyList.semigroup()).map(va, vb, fn).fix().toEither()
Let's say I have a similar data type to Validated called ValRes
sealed class ValRes<out E, out A> {
data class Valid<A>(val a: A) : ValRes<Nothing, A>()
data class Invalid<E>(val e: E) : ValRes<E, Nothing>()
}
If I have two values of type ValRes and I want to combine them accumulating the errors I could write a function like this:
fun <E, A, B> tupled(
a: ValRes<E, A>,
b: ValRes<E, B>,
combine: (E, E) -> E
): ValRes<E, Pair<A, B>> =
if (a is Valid && b is Valid) valid(Pair(a.a, b.a))
else if (a is Invalid && b is Invalid) invalid(combine(a.e, b.e))
else if (a is Invalid) invalid(a.e)
else if (b is Invalid) invalid(b.e)
else throw IllegalStateException("This is impossible")
if both values are Valid I build a pair of the two values
if one of them is invalid, I get a new Invalid instance with the single value
if both are invalid, I use the combine function to build Invalid instance containing both values.
Usage:
tupled(
validateEmail("stojan"), //invalid
validateName(null) //invalid
) { e1, e2 -> "$e1, $e2" }
This works in a generic way, independent of the types E, A and B. But it only works for two values. We could build such a function for N values of type ValRes.
Now back to arrow:
Validated.applicative<Nel<E>>(NonEmptyList.semigroup()).map(va, vb, fn).fix().toEither()
tupled is similar to map (with hardcoded success function). va and vb here are similar to a and b in my example. Instead of returning a pair of values, here we have a custom function (fn) that combines the two values in case of success.
Combining the errors:
interface Semigroup<A> {
/**
* Combine two [A] values.
*/
fun A.combine(b: A): A
}
Semigroup in arrow is a way for combining two values from the same type in a single value of that same type. Similar to my combine function. NonEmptyList.semigroup() is the implementation of Semigroup for NonEmptyList that given two lists adds the elements together into a single NonEmptyList.
To sum up:
If both values are Valid -> it will combine them using the supplied function
If one value is Valid and one Invalid -> gives back the error
If both values are Invalid -> Uses the Semigroup instance for Nel to combine the errors
Under the hood this scales for 2 up to X values (22 I believe).

Kotlin - Type of `if` and `when` Expressions

I understand that Kotlin is a statically-typed language, and all the types are defined at the compile time itself.
Here is a when expression that returns different types:
fun main(){
val x = readLine()?.toInt() ?: 0
val y = when(x){
1 -> 42
2 -> "Hello"
else -> 3.14F
}
println(y::class.java)
}
During runtime (Kotlin 1.3.41 on JVM 1.8) this is the output:
When x = 1, it prints class java.lang.Integer
When x = 2, it prints class java.lang.String
Otherwise, it prints class java.lang.Float
When does the compiler determine the type of y? Or, how does the compiler infers the type of y during compile-time?
Actually, the type of the when expression resolves to Any in this case, so the y variable can have any value. An IDE even warns you, that Conditional branch result of type X is implicitly cast to Any, at least Android Studio does, as well as Kotlin Playground.
The type of that variable for you is Any (as the smallest possible superclass for all that types), but underlying value is untouched.
What does it mean? You can safely access only properties that are common for all that types (so only properties available for Any type. And property ::class.java is available for all types.
See this example - I use some other types to good visualise what is it about.
abstract class FooGoo {
fun foogoo(): String = "foo goo"
}
class Foo: FooGoo() {
fun foo(): String = "foo foo"
}
class Goo: FooGoo() {
fun goo(): String = "goo goo"
}
class Moo {
fun moo(): String = "moo moo"
}
fun main(x: Int) {
val n = when (x) {
0 -> Foo()
1 -> Goo()
else -> throw IllegalStateException()
} // n is implicitly cast to FooGoo, as it's the closes superclass of both, Foo and Goo
// n now has only methods available for FooGoo, so, only `foogoo` can be called (and all methods for any)
val m = when (x) {
0 -> Foo()
1 -> Goo()
else -> Moo()
} // m is implicitly cast to Any, as there is no common supertype except Any
// m now has only methods available for Any() - but properties for that class are not changed
// so, `m::class.java` will return real type of that method.
println(m::class.java) // // Real type of m is not erased, we still can access it
if (m is FooGoo) {
m.foogoo() // After explicit cast we are able to use methods for that type.
}
}
During compile-time, the inferred type of y is Any which is the supertype of all types in Kotlin. During run-time, y can reference [literally] any type of object. The IDE generates a warning "Conditional branch result of type Int/String/Float is implicitly cast to Any".
In the example,
When x = 1, it refers to an object of type java.lang.Integer.
When x = 2, it refers to an object of type java.lang.String.
Otherwise, it refers to an object of type java.lang.Float.
Thanks Slaw for the quick explanation:
There's a difference between the declared type of a variable and the actual type of the object it references. It's no different than doing val x: Any = "Hello, Wold!";

Using Kotlin streams and filter throws a cannot cast to my custom type exception

I am exploring the equivalent of C# Linq but in Kotlin, so I came across streams:
val c = Manager.customers.entries.stream()
.filter { x -> x.value.name == "Jaime Garcia" }
// after this c is a ReferencePipeline type... apparently. Consider also that Manager.customers is of type HashMap().
And then when I use it:
val id = (c as Customer)?.id
... it throws an error :
java.lang.ClassCastException : java.util.stream.ReferencePipeline$2
cannot be cast to com.example.mypackage.Customer
Maybe I should map c otherwise before using, I tried with something like .collect(Collectors.toMap(a -> a.getValue())) appended after the filter but syntactically it doesn't even work
You should not be using Java's stream API in Kotlin *. Instead you should be using what the Kotlin standard library offers to you to work with collections.
If your customers map looks like this:
val customers = mapOf(1 to Customer("Foo"), 2 to Customer("Bar"))
you can retrieve a list of matching Customers like this:
val c = customers.values.filter { x -> x.name == "Foo" }
Note that c will be a List.
If you want a single element right away, use firstOrNull (for example):
val foo = customers.values.firstOrNull { x -> x.name == "Foo" }
val id = c?.id // id will be null or a Customer
At least as long as you can avoid it. I just found out that the Kotlin standard library offers utility functions to work with Java 8 streams, so it seams that it is not discouraged.
If you want to use proper Kotlin syntax, you can also use find:
Manager.customers.values
.find { it.name == "Jaime Garcia" }
?.id

Function definition: fun vs val

I'm curious about what is the suggested way to define member functions in Kotlin. Consider these two member functions:
class A {
fun f(x: Int) = 42
val g = fun(x: Int) = 42
}
These appear to accomplish the same thing, but I found subtle differences.
The val based definition, for instance, seems to be more flexible in some scenarios. That is, I could not work out a straight forward way to compose f with other functions, but I could with g. To toy around with these definitions, I used the funKTionale library. I found that this does not compile:
val z = g andThen A::f // f is a member function
But if f were defined as a val pointing to the same function, it would compile just fine. To figure out what was going on I asked IntelliJ to explicitly define the type of ::f and g for me, and it gives me this:
val fref: KFunction1<Int, Int> = ::f
val gref: (Int) -> Int = g
So one is of type KFunction1<Int, Int>, the other is of type (Int) -> Int. It's easy to see that both represent functions of type Int -> Int.
What is the difference between these two types, and in which cases does it matter? I noticed that for top-level functions, I can compose them fine using either definition, but in order to make the aforementioned composition compile, I had to write it like so:
val z = g andThen A::f.partially1(this)
i.e. I had to partially apply it to this first.
Since I don't have to go through this hassle when using vals for functions, is there a reason why I should ever define non-Unit member functions using fun? Is there a difference in performance or semantics that I am missing?
Kotlin is all about Java interoperability and defining a function as a val will produce a completely different result in terms of the interoperability. The following Kotlin class:
class A {
fun f(x: Int) = 42
val g = fun(x: Int) = 42
}
is effectively equivalent to:
public class A {
private final Function1<Integer, Integer> gref = new Function1<Integer, Integer>() {
#Override
public Integer invoke(final Integer integer) {
return 42;
}
};
public int f(final int value) {
return 42;
}
public Function1<Integer, Integer> getG() {
return gref;
}
}
As you can see, the main differences are:
fun f is just a usual method, while val g in fact is a higher-order function that returns another function
val g involves creation of a new class which isn't good if you are targeting Android
val g requires unnecessary boxing and unboxing
val g cannot be easily invoked from java: A().g(42) in Kotlin vs new A().getG().invoke(42) in Java
UPDATE:
Regarding the A::f syntax. The compiler will generate an extra Function2<A, Integer, Integer> class for every A::f occurrence, so the following code results in two extra classes with 7 methods each:
val first = A::f
val second = A::f
Kotlin compiler isn't smart enough at the moment to optimize such kind of things. You can vote for the issue here https://youtrack.jetbrains.com/issue/KT-9831. In case you are interested, here is how each class looks in the bytecode: https://gist.github.com/nsk-mironov/fc13f2075bfa05d8a3c3
Here's some code showing how f and g are different when it comes to usage:
fun main(args: Array<String>) {
val a = A()
exe(a.g) // OK
//exe(a.f) // does not compile
exe { a.f(it) } // OK
}
fun exe(p: (Int) -> Int) {
println(p(0))
}
Where f and g are:
fun f(x: Int) = 42
val g = fun(x: Int) = 42
You can see that g is an object that can be used like a lambda, but f cannot. To use f similarly, you have to wrap it in a lambda.