I'm getting a Kotlin Error:
The feature "unit conversions on arbitrary expressions" is
experimental and should be enabled explicitly. You can also change the
original type of this expression to (...) -> Unit
My code is the following:
val foo: () -> String = { "Test" }
fun bar(doSometing: () -> Unit) { /* */ }
val baz = bar(foo) // here foo throws the error
It's pretty obvious what i'm doing wrong: bar expects () -> Unit, but i provide () -> String.
However, the error message implies i can opt in to "unit conversions on arbitrary expressions". How do i do this?
The only thing related i could find here on SO doesn't answer my question: https://stackoverflow.com/questions/72238339/kotlin-the-feature-unit-conversion-is-disabled
It's interesting that you can pass a function reference but not an equivalent expression:
fun f(): String = "Test"
val foo = ::f
fun bar(doSometing: () -> Unit) { /* */ }
val baz = bar(::f) // OK
val baz2 = bar(foo) // Error
You can make it compile using the command-line option
-XXLanguage:+UnitConversionsOnArbitraryExpressions but it's not recommended:
$ cat t.kt
val foo: () -> String = { "Test" }
fun bar(doSometing: () -> Unit) { /* */ }
val baz = bar(foo)
$ kotlinc t.kt
t.kt:5:15: error: the feature "unit conversions on arbitrary expressions" is experimental and should be enabled explicitly. You can also change the original type of this expression to (...) -> Unit
val baz = bar(foo)
^
$ kotlinc -XXLanguage:+UnitConversionsOnArbitraryExpressions t.kt
warning: ATTENTION!
This build uses unsafe internal compiler arguments:
-XXLanguage:+UnitConversionsOnArbitraryExpressions
This mode is not recommended for production use,
as no stability/compatibility guarantees are given on
compiler or generated code. Use it at your own risk!
t.kt:3:9: warning: parameter 'doSometing' is never used
fun bar(doSometing: () -> Unit) { /* */ }
^
$
This is because the expected type of doSometing is () -> Unit, but given () -> String, you can fix it by modify return type of foo or modify type of doSometing
both
val foo: () -> Unit = { println("Test") }
fun bar(doSometing: () -> Unit) { doSometing() }
val baz = bar(foo)
.
val foo: () -> String = { "Test" }
fun bar(doSometing: () -> String) { /* */ }
val baz = bar(foo)
works fine.
Related
We have a relatively simple builder pattern we use for test data generator in Kotlin.
The builders follow the pattern:
class ThingBuilder private constructor(
var param1: Int = 1,
var param2: Boolean = true
) {
private constructor(vararg inits: ThingBuilder.(ThingBuilder) -> Unit) : this() {
inits.forEach { it(this) }
}
fun build(): Thing {
return Thing(
param1,
param2
)
}
companion object {
fun asDefaultCase(init: ThingBuilder.(ThingBuilder) -> Unit = {}): ThingBuilder {
return ThingBuilder(init)
}
fun asSomethingElseCase(init: ThingBuilder.(ThingBuilder) -> Unit = {}): ThingBuilder {
return ThingBuilder({ b -> b.param2 = false }, init)
}
}
}
Here the Kotlin compiler reports a warning:
The expression is unused
which references the line:
inits.forEach { it(this) }
I've tried turning that into an Array<T> rather than varags but same warning occurs.
What would be the more correct way to make this structure where the consumers can pass in lambdas to configure the builder data?
(for reference, the code works correctly and the loop functions as expected)
This seems to be a rather old bug KT-21282 False positive UNUSED_EXPRESSION compiler warning with object and lambda with receiver / extension function type.
The fix is simple - just specify the explicit receiver and do this.it(this). I also don't see why you would need to pass this as both the receiver and the formal parameter to the block. I would just do this instead:
private constructor(vararg inits: ThingBuilder.() -> Unit) : this() {
inits.forEach { this.it() }
}
or:
private constructor(vararg inits: ThingBuilder.() -> Unit) : this() {
inits.forEach { it(this) }
}
Then you don't even need to write the b parameter in asSomethingElseCase:
fun asSomethingElseCase(init: ThingBuilder.() -> Unit = {}): ThingBuilder {
return ThingBuilder({ param2 = false }, init)
}
I'm trying to introduce the following (simplified) DSL:
fun <T> myDsl(specFn: DslSpec<T>.() -> Unit) {
val value = DslSpec<T>().apply(specFn).fn!!()
println("value is: $value")
}
class DslSpec<T> {
internal var fn: (() -> T)? = null
fun getValue(fn: () -> T) {
this.fn = fn
}
}
fun testCase() {
myDsl {
getValue {
"abc"
}
}
}
But it fails to infer T based just on the returned type of getValue ("Not enough information to infer type variable T"). I kind of see how it could be a very hard task to do for a compiler, but thought maybe there are already some tricks to make constructs like this work?
If you're using a version of Kotlin < 1.6.0, you should add #BuilderInference to the specFn argument:
fun <T> myDsl(#BuilderInference specFn: DslSpec<T>.() -> Unit) {
...
}
https://pl.kotl.in/__xy04j88
If you're using a version >= 1.6.0, you should either use the annotation as well, or both your declarations and their usages must be compiled with the compiler argument -Xenable-builder-inference.
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, _> { "" }
I am trying to use the Option.getOrElse() method.
According to the source:
inline fun <R> fold(ifEmpty: () -> R, ifSome: (A) -> R): R = when (this) {
is None -> ifEmpty()
is Some<A> -> ifSome(t)
}
fun <T> Option<T>.getOrElse(default: () -> T): T = fold({ default() }, ::identity)
But when I call getOrElse with a lambda that returns a value of type other than type T, it does not show any error compile time or runtime.
val value1 = Some("val")
// No error
value1.getOrElse { true }
It does not seem right. What am I doing wrong?
This is because Option is covariant (you can see it's declared as Option<out A>), so value1 is also an Option<Any> and { true } is inferred to be () -> Any.
I have following functions:
fun <T, U> process(t: T, call: (U) -> Unit, map: (T) -> U) = call(map(t))
fun <T> processEmpty(t: T, call: () -> Unit) = process(t, call, {}) // error
but the processEmpty is not compiling. The error message is Type mismatch: inferred type is () -> kotlin.Unit but (kotlin.Unit) -> kotlin.Unit was expected. But if I change this function to
fun <T> processEmpty2(t: T, call: (Unit) -> Unit) = process(t, call, {}) // OK
So what's the difference between () -> Unit and (Unit) -> Unit types? Why first version of processEmpty isn't compiling?
Unit is actually a type that has exactly one value (the value is Unit itself; also, this is why it is named Unit). It corresponds to void in Java, but it's not the same.
Kotlin compiler treats functions with no declared return value as Unit-returning functions, and return Unit can also be omitted. This is why { } is a Unit-returning function.
But this is not applied to arguments. To be strict, when you declare a function with Unit argument or (Unit) -> Unit function variable, you have to pass an argument of type Unit at call site. The only value to pass is Unit.
A lambda with no specified arguments like { doSomething() } is treated both as a function with no arguments and as a function with single implicit argument it. You can use { } both as () -> Unit and (Unit) -> Unit.
As to the call site, as said above, Unit has to be passed:
val f: (Unit) -> Unit = { println("Hello") }
f(Unit) // the only valid call
Whereas () -> Unit functions do not need an argument to be passed:
val f: () -> Unit = { println("Hello") }
f() // valid call
In your example, type inference happens as follows:
fun <T, U> process(t: T, call: (U) -> Unit, map: (T) -> U) = call(map(t))
fun <T> processEmpty(t: T, call: () -> Unit) = process(t, call, {}) // error
map: (T) -> U = { }, thus a replacement for U is Unit returned from { }.
Therefore call should be (Unit) -> Unit.
call: () -> Unit which is not the same to (Unit) -> Unit, as stated above. Error.