Search code examples
gradlekotlinkaptarrow-kt

Unable to generate objects for both @higherkind and @extension


I define two objects:

  1. data class ParserK annotated with @higherkind
  2. interface ParserKFunctor annotated with @extension

Here is the code:

@higherkind
data class ParserK<A>(val f: (String) -> Option<A>): ParserKOf<A> {
    companion object
}

@extension
interface ParserKFunctor : Functor<ForParserK> {
    override fun <A, B> Kind<ForParserK, A>.map(f: (A) -> B): Kind<ForParserK, B> {
        ...
    }
}

When I execute ./gradlew :app:kaptKotlin I get:

error: "Arrow's annotations can only be used on Kotlin classes". Not valid for error.NonExistentClass

> Task :app:kaptGenerateStubsKotlin

> Task :app:kaptKotlin FAILED
e: error: Arrow's annotations can only be used on Kotlin classes. Not valid for error.NonExistentClass                                           

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:kaptKotlin'.
> Compilation error. See log for more details

Here is what I discovered:

  1. If I remove the functor definition then the goal completes successfully and I can see the generated code.
  2. If I remove @higherkind from the data class ParserK and copy the generated sources into the same file where ParserK is defined then I can see the generated code for the functor.

It seems like a bug for me, correct me if I am wrong, please

UPDATED:

  1. Here is the link to the repository with my code: repository
  2. The issue on the bug tracker is here

Solution

  • (For arrow-version 0.9.1-SNAPSHOT and prior)

    The Higherkinded Processor and Extension Processors have a dependency. Correctly, the extension annotation depends on the code generated by the higherkinded annotation. Why check this link.

    The brief summary is, whenever you try implementing typeclasses the compiler needs the Higherkinded Types of your datatype.

    @extension
    interface ListKFunctor : Functor<ForListK> {
    //                                ^^^^^^^^
    //                    This exists after building your module
      override fun <A, B> Kind<ForListK, A>.map(f: (A) -> B): Kind<ForListK, B> {
        return this.fix().map(f)
      }
    }
    

    The simplest answer to this question is:

    Always separate your Higherkinded Types from your typeclass definitions.

    But Arrow is experimenting with other options on Codegen. Meaning in future releases this problem will be solved.