Oğuzhan Soykan
12/09/2024, 9:05 AMNonEmptyList
has @JvmInline
annotation, with this change mock
libraries can not mock if NonEmptyList
is used as a function parameter. Example:
interface Operation {
suspend fun execute(items: NonEmptyList<String>): Either<Throwable, Unit>
}
class NonEmptyListTests : ShouldSpec({
should("mocking") {
val operation = mockk<Operation> {
coEvery { execute(any()) } returns Unit.right()
}
operation.execute(NonEmptyList("a", listOf("b", "c"))) shouldBe Unit.right()
}
})
Fails with the exception (less stack trace for brevity):
Caused by: java.lang.TypeNotPresentException: Type <unknown> not present
at net.bytebuddy.description.type.TypeDefinition$Sort.describeOrNull(TypeDefinition.java:295)
at net.bytebuddy.description.type.TypeDescription$Generic$OfWildcardType$ForLoadedType$WildcardUpperBoundTypeList.get(TypeDescription.java:4841)
at net.bytebuddy.description.type.TypeDescription$Generic$OfWildcardType$ForLoadedType$WildcardUpperBoundTypeList.get(TypeDescription.java:4814)
at net.bytebuddy.matcher.FilterableList$AbstractBase.getOnly(FilterableList.java:141)
The reason I believe Inline Classes are not open to generating proxies from them.
The workaround I did was change my codebase into List
version of the implementation, but there might be people who might get affected.phldavies
12/09/2024, 4:18 PMlateinit var
Manuel
12/09/2024, 6:16 PMbad class file: ~/path/to/class.kt
undeclared type variable: A
Please remove or make sure it appears in the correct subdirectory of the classpath.
I was curious if it would be a reasonable request to have NonEmptySet
and NonEmptyList
changed to interfaces and have all factories that generate instances of these return an inline
implementation?
I know this isn't really an Arrow issue but I can't think of any good workarounds to this. I've ended up having to change most references to NonEmptyCollection
for compilation to work, which is acceptable but I end up needing to cast or change the type of lists or sets more frequently and I feel that my API's aren't as clear.
Either
way, I'm open to `Option`s. 😅
See what I did there? 😂phldavies
12/09/2024, 8:54 PMList<T>
or Set<T>
interfaces (or a supertype of), and thus will always be a boxed instance.Alejandro Serrano.Mena
12/10/2024, 7:57 AMNonEmptyList
directly quite often, and in that case the inline implementation directly calls the function in the wrapper value, without having to perform additional boxingAlejandro Serrano.Mena
12/10/2024, 8:15 AMSchedule
)
in the past it seems that aligning the names of the generic parameters with that of the underlying type makes Dagger happy, so I'm trying that as part of https://github.com/arrow-kt/arrow/pull/3549Alejandro Serrano.Mena
12/10/2024, 8:19 AMOğuzhan Soykan
12/10/2024, 8:43 AMphldavies
12/10/2024, 9:34 AMimport io.mockk.mockk
import kotlin.test.Test
@JvmInline
value class Box<E>(private val value: List<E>): List<E> by value
interface Example {
fun handle(box: Box<String>)
}
class ValueClassMockksTest {
@Test
fun `can mockk it`() {
mockk<Example>()
}
}
and #C9EJFT6DB still doesn't like it 🤔
Then again, we've encountered numerous edge cases with value classes and mockk...phldavies
12/10/2024, 10:15 AM@JvmInline value class
over just having them being a normal class. There are (minor) drawbacks to value classes (such as not being able to use one in a lateinit var
).
I'm concerned that every time a NonEmptyList
or NonEmptySet
is used as one of it's super-types it will be boxed afresh. For example, nel.forEach { println(it) }
will invoke NonEmptyList.box-impl
and cast it to Iterable
(according to the bytecode)Alejandro Serrano.Mena
12/10/2024, 11:08 AMAlejandro Serrano.Mena
12/10/2024, 11:13 AMforEach
some overhead comes from the boxing (I'm not sure why, though, since NonEmptyList
is already an Iterable
)phldavies
12/10/2024, 12:13 PMIterable
in this case) - although NonEmptyList
implements it via delegation, the compiler can't assume the value class doesn't override (or wouldn't in the future) any methods from that interface, so it can't simply leave it unboxed.Manuel
12/10/2024, 6:06 PMAlejandro Serrano.Mena
12/11/2024, 8:22 AM2.0.1-alpha.1
with the change is now in Maven Central. Please tell me if this helps with your problem 🙏Manuel
12/11/2024, 9:42 AM> Task :project:module:compileDebugJavaWithJavac FAILED
/project/module/build/generated/ksp/debug/java/path/to/class/Implementation_Factory.java:27: error: cannot access Implementation
public final class Implementation_Factory implements Factory<Implementation> {
^
bad class file: /project/module/build/tmp/kotlin-classes/debug/path/to/class/Implementation.class
undeclared type variable: E
Please remove or make sure it appears in the correct subdirectory of the classpath.
1 error
And the class basically looks like this:
import arrow.core.Nel
import arrow.core.toNonEmptyListOrNull
import javax.inject.Inject
class Implementation @Inject constructor() {
private fun doSomething(nel: Nel<*>) = Unit
}
Would it help if I provided a simple project that replicates this issue for you to experiment with? And can I do anything else to help try to resolve this issue?Alejandro Serrano.Mena
12/11/2024, 9:55 AMManuel
12/11/2024, 5:53 PMManuel
12/13/2024, 7:40 AMokarm
12/23/2024, 9:23 AMRaise<...>.f()
, which I cannot fix by changing parameter types in my own code.Alejandro Serrano.Mena
12/23/2024, 3:15 PMokarm
12/23/2024, 5:18 PMNonEmptyList
can be replaced with List
to make the code compile. But in 2.0.1-alpha that same error starts manifesting itself in Raise
, which cannot be replaced by a "different Raise" - there is no replacement. So that makes it worse.
bad class file: /Users/ondra/.gradle/caches/modules-2/files-2.1/io.arrow-kt/arrow-core-jvm/2.0.1-alpha.1/73049529baecebbf3f002a5e1f27ea43298e3e5b/arrow-core-jvm-2.0.1-alpha.1.jar(/arrow/core/raise/Raise.class)
undeclared type variable: E