Arrow: ClassCastException when using state transformer with ListK

Created on 5 May 2018  路  13Comments  路  Source: arrow-kt/arrow

I am using a state transformer with ListK. I see a ClassCastException that I can reproduce with the minimum failing code below.

Kotlin version: 1.2.41
Arrow version: 0.7.1

import arrow.core.*
import arrow.data.*
import arrow.typeclasses.binding
typealias AnyStack = List<Pair<String, Any>>
fun main(args : Array<String>) {

    fun <T:Any> genList(name:String, list: List<T>) = StateT<ForListK, AnyStack, T>(ListK.monad()) { stack ->
        ListK(list).map {
            stack.plus(name to it) toT it
        }
    }

    fun <T0:Any, T1:Any> genList(name0:String, name1:String, list: List<Tuple2<T0, T1>>) =
            StateT<ForListK, AnyStack, Tuple2<T0, T1>>(ListK.monad()) { stack ->
                ListK(list).map {
                    stack.plus(name0 to it.a).plus(name1 to it.b) toT it
                }
            }

    fun intStackOperations() = StateT.monad<ForListK, AnyStack>(ListK.monad()).binding {
        val (a,d) = genList("a", "d", listOf(Tuple2(0,"h"))).bind() // no ClassCastException if I comment this line
        val b = genList("b", listOf(1,2,3)).bind()
        genList("c",(0 until b).toList()).bind()
    }.fix()    
    println(intStackOperations().runS(ListK.monad(), emptyList()).fix())
}

Please let me know if you need more information.

upstream

Most helpful comment

@pakoito looking forward to hearing more about it.

All 13 comments

Can you please paste the full stacktrace? I'd like to see which types are being casted. That Any doesn't look great, specially inside coroutines :D

Thanks for your quick response! I wasnt aware of issues with Any in coroutines.

Here is the stack trace.

Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to arrow.typeclasses.MonadContinuation
    at ExampleKt$main$3$1.doResume(example.kt:24)
    at kotlin.coroutines.experimental.jvm.internal.CoroutineImpl.resume(CoroutineImpl.kt:54)
    at arrow.typeclasses.MonadContinuation$bind$$inlined$suspendCoroutineOrReturn$lambda$1.invoke(MonadContinuations.kt:59)
    at arrow.typeclasses.MonadContinuation$bind$$inlined$suspendCoroutineOrReturn$lambda$1.invoke(MonadContinuations.kt:14)
    at arrow.data.StateT$flatMap$$inlined$run$lambda$1$1$1.invoke(StateT.kt:221)
    at arrow.data.StateT$flatMap$$inlined$run$lambda$1$1$1.invoke(StateT.kt:39)
    at arrow.data.ListK.flatMap(ListK.kt:14)
    at arrow.instances.ListKMonadInstance$DefaultImpls.flatMap(listk.kt:69)
    at arrow.data.Instance_arrow_instances_ListKMonadInstanceKt$monad$1.flatMap(instance.arrow.instances.ListKMonadInstance.kt:7)
    at arrow.data.Instance_arrow_instances_ListKMonadInstanceKt$monad$1.flatMap(instance.arrow.instances.ListKMonadInstance.kt:7)
    at arrow.data.StateT$flatMap$$inlined$run$lambda$1$1.invoke(StateT.kt:220)
    at arrow.data.StateT$flatMap$$inlined$run$lambda$1$1.invoke(StateT.kt:39)
    at arrow.core.PredefKt$andThen$1.invoke(predef.kt:9)
    at arrow.data.StateT$run$$inlined$run$lambda$1.invoke(StateT.kt:277)
    at arrow.data.StateT$run$$inlined$run$lambda$1.invoke(StateT.kt:39)
    at arrow.data.ListK.flatMap(ListK.kt:14)
    at arrow.instances.ListKMonadInstance$DefaultImpls.flatMap(listk.kt:69)
    at arrow.data.Instance_arrow_instances_ListKMonadInstanceKt$monad$1.flatMap(instance.arrow.instances.ListKMonadInstance.kt:7)
    at arrow.data.Instance_arrow_instances_ListKMonadInstanceKt$monad$1.flatMap(instance.arrow.instances.ListKMonadInstance.kt:7)
    at arrow.data.StateT.run(StateT.kt:277)
    at arrow.data.StateTKt.runM(StateT.kt:27)
    at arrow.data.StateT$flatMap$$inlined$run$lambda$1$1$1.invoke(StateT.kt:221)
    at arrow.data.StateT$flatMap$$inlined$run$lambda$1$1$1.invoke(StateT.kt:39)
    at arrow.data.ListK.flatMap(ListK.kt:14)
    at arrow.instances.ListKMonadInstance$DefaultImpls.flatMap(listk.kt:69)
    at arrow.data.Instance_arrow_instances_ListKMonadInstanceKt$monad$1.flatMap(instance.arrow.instances.ListKMonadInstance.kt:7)
    at arrow.data.Instance_arrow_instances_ListKMonadInstanceKt$monad$1.flatMap(instance.arrow.instances.ListKMonadInstance.kt:7)
    at arrow.data.StateT$flatMap$$inlined$run$lambda$1$1.invoke(StateT.kt:220)
    at arrow.data.StateT$flatMap$$inlined$run$lambda$1$1.invoke(StateT.kt:39)
    at arrow.core.PredefKt$andThen$1.invoke(predef.kt:9)
    at arrow.data.StateT$run$$inlined$run$lambda$1.invoke(StateT.kt:277)
    at arrow.data.StateT$run$$inlined$run$lambda$1.invoke(StateT.kt:39)
    at arrow.data.ListK.flatMap(ListK.kt:14)
    at arrow.instances.ListKMonadInstance$DefaultImpls.flatMap(listk.kt:69)
    at arrow.data.Instance_arrow_instances_ListKMonadInstanceKt$monad$1.flatMap(instance.arrow.instances.ListKMonadInstance.kt:7)
    at arrow.data.Instance_arrow_instances_ListKMonadInstanceKt$monad$1.flatMap(instance.arrow.instances.ListKMonadInstance.kt:7)
    at arrow.data.StateT.run(StateT.kt:277)
    at arrow.data.StateT.runS(StateT.kt:297)
    at ExampleKt.main(example.kt:30)

I modified my code to eschew the Any type by replacing it with a string as follows:

import arrow.core.*
import arrow.data.*
import arrow.typeclasses.binding

typealias AnyStack = List<Pair<String, String>>

fun main(args : Array<String>) {

    fun <T:Any> genList(name:String, list: List<T>) = StateT<ForListK, AnyStack, T>(ListK.monad()) { stack ->
        ListK(list).map {
            stack.plus(name to "$it") toT it
        }
    }

    fun <T0:Any, T1:Any> genList(name0:String, name1:String, list: List<Tuple2<T0, T1>>) =
            StateT<ForListK, AnyStack, Tuple2<T0, T1>>(ListK.monad()) { stack ->
                ListK(list).map {
                    stack.plus(name0 to "${it.a}").plus(name1 to "${it.b}") toT it
                }
            }

    fun intStackOperations() = StateT.monad<ForListK, AnyStack>(ListK.monad()).binding {
        val (a,d) = genList("a", "d", listOf(Tuple2(0,"h"))).bind() // no ClassCastException if I comment this line
        val b = genList("b", listOf(1,2,3)).bind()
        genList("c",(0 until b).toList()).bind()
    }.fix()

    println(intStackOperations().runS(ListK.monad(), emptyList()).fix())
}

I still see the exceptionwith a similar stack trace though.

Let's rule out coroutines. Can you please rewrite the comprehension to use flatMap instead?

Thats a great suggestion. I rewrote with flatMap. This doesnt throw the exception!

   fun intStackOperations() =
         genList("a", "d", listOf(Tuple2(0, "h"))).flatMap(ListK.monad()){
            genList("b", listOf(1, 2, 3)).flatMap(ListK.monad()){
                genList("c", (0 until it).toList())
            }
        }

FWIW, I see that the following implementation with comprehensions works. The only difference is that I dont have a val (a,d) assignment in the first comprehension.

    fun intStackOperations() = StateT.monad<ForListK, AnyStack>(ListK.monad()).binding {
//        val (a,d) = genList("a", "d", listOf(Tuple2(0,"h"))).bind() // ClassCastException with this line
        genList("a", "d", listOf(Tuple2(0,"h"))).bind() // no ClassCastException 
        val b = genList("b", listOf(1,2,3)).bind()
        genList("c",(0 until b).toList()).bind()
    }.fix()

I tried a bit of debugging myself but couldnt figure out what was causing this exception.

Can you please hint the types for (a,d)? It may be a case where type inference just farted.

Thanks for your time! Sorry for the absurd names of the variables.

I tried your suggestion and still see an exception.

fun intStackOperations() = StateT.monad<ForListK, AnyStack>(ListK.monad()).binding {
        val (a:Int, d:String) = genList("a", "d", listOf(Tuple2(0,"h"))).bind() // ClassCastException with this line
//        genList("a", "d", listOf(Tuple2(0,"h"))).bind() // no ClassCastException
        val b = genList("b", listOf(1,2,3)).bind()
        genList("c",(0 until b).toList()).bind()
    }.fix()

Try not to destructure it.

I was trying it out as I saw your reply :-). Not destructuring it and also supplying type hint still has the exception.

    fun intStackOperations() = StateT.monad<ForListK, AnyStack>(ListK.monad()).binding {
//        val (a:Int, d:String) = genList("a", "d", listOf(Tuple2(0,"h"))).bind() // ClassCastException with this line
        val a_d:Tuple2<Int, String> = genList("a", "d", listOf(Tuple2(0,"h"))).bind() // ClassCastException with this line
//        genList("a", "d", listOf(Tuple2(0,"h"))).bind() // no ClassCastException
        val b = genList("b", listOf(1,2,3)).bind()
        genList("c",(0 until b).toList()).bind()
    }.fix()

Okay, I believe with this info you can raise a ticket to JetBrains about coroutines rewritting code with the wrong type inference :D Try here: https://youtrack.jetbrains.com/issues/kt

FYI, the JetBrains ticket got a response. To summarize, they say "Basically, the issue is caused by unconventional use of coroutines in the library (even if this particular bug is fixed on the library side, this use case of coroutines anyway seems to be rather unsupported)."

@pakoito looking forward to hearing more about it.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

JorgeCastilloPrz picture JorgeCastilloPrz  路  3Comments

vejeta picture vejeta  路  3Comments

pakoito picture pakoito  路  4Comments

raulraja picture raulraja  路  4Comments

JorgeCastilloPrz picture JorgeCastilloPrz  路  3Comments