Mikhail Bolotov
10/22/2021, 6:41 PMfun <S> create(code: () -> S): S {
println("string")
return code()
}
fun create(code: () -> Unit) {
println("unit")
}
fun main() {
create { "a" } // prints unit
val a = { "a" }
create(a) // prints string
}jimn
10/23/2021, 9:19 PMfun <S> create(code: () -> S): S {
println("string")
return code()
}
fun <S:Unit>create(code: () -> S):S {
println("unit")
}
fun main( ) {
create { "a" } // prints unit
val a = { "a" }
create(a) // prints string
}ephemient
10/24/2021, 3:49 AMval a = { "a" }
is inferred to be
val a: () -> String = { "a" }
which then means that only create<String> is callableephemient
10/24/2021, 3:50 AMcreate { "a" }
resolve to `create`(Unit)?ephemient
10/24/2021, 3:52 AMcreate overloads are applicable, so it picks the most specific onejimn
10/24/2021, 3:55 AM{"a"} in create({"a"}) does not resolve to create<Unit>(()->S):Sjimn
10/24/2021, 4:10 AMinline fun <reified S : Unit> create(code: () -> S): S {
println("unit")
return code() as S
}
permits {"a"}ephemient
10/24/2021, 4:11 AMjimn
10/24/2021, 4:12 AMephemient
10/24/2021, 4:14 AMjimn
10/24/2021, 4:17 AM>> {"a"}res0: () -> kotlin.String = () -> kotlin.String
>>
jimn
10/24/2021, 4:20 AMephemient
10/24/2021, 4:24 AMephemient
10/24/2021, 4:24 AMjimn
10/24/2021, 4:24 AM>> fun <S> create(code: () -> S): S {... println("string") ... return code() ... }
>>
>> inline fun <reified S : Unit> create(code: () -> S): S {... println("unit") ... return code() as S ... }
>>
>> fun main() {... create { "a" } // prints unit ... val a = { "a" } ... create(a) // prints string ... }
>> main()unit string
>>
ephemient
10/24/2021, 4:26 AMcreate<Unit>{ "a" } works, so it is picked, but create<Unit>(a) doesn'tjimn
10/24/2021, 4:30 AMjimn
10/24/2021, 4:31 AMjimn
10/24/2021, 4:34 AMephemient
10/24/2021, 4:37 AMfun <S: Unit> create, and then you write create<String> which doesn't fit. would be fine if you defined the unit-create without the type parameterephemient
10/24/2021, 4:38 AMephemient
10/24/2021, 4:40 AMjimn
10/24/2021, 4:47 AM>> fun ((Int) -> Unit).f() = Unit
>> {x:Int->}.f()
>>
jimn
10/24/2021, 5:11 AM>>> create({"a"}as ()->Nothing?)
string
java.lang.ClassCastException: class java.lang.String cannot be cast to class java.lang.Void (java.lang.String and java.lang.Void are in module java.base of loader 'bootstrap')
>>> create({"a"}as ()->Nothing)
unit
java.lang.ClassCastException: class java.lang.String cannot be cast to class kotlin.Unit (java.lang.String is in module java.base of loader 'bootstrap'; kotlin.Unit is in unnamed module of loader java.net.URLClassLoader @484203f3)
>>> create({"a"}as ()->Int)
string java.lang.ClassCastException: class java.lang.String cannot be cast to class java.lang.Number (java.lang.String and java.lang.Number are in module java.base of loader 'bootstrap')jimn
10/24/2021, 5:13 AMNothing? fails to match Unit but Nothing matches, and String matches, but Int does not match, ok now i know the discriminator.jimn
10/24/2021, 5:35 AMephemient
10/24/2021, 7:20 AMval a: () -> Unit = { "a" }
val b: () -> String = { "b" }
val x = a() // Unit
val y = b() // "b"
val c = (a as () -> String)() // ClassCastException
the { "a" } and { "b" } expressions look similar but create different types of functions (although erasure means the cast of the function succeeds, it's handling the result that fails)ephemient
10/24/2021, 7:21 AMval a: (Any?) -> Unit = { "a" } // ok, "it" isn't used
val b = { "b" } as (Any?) -> Unit // ClassCastExceptionjimn
10/24/2021, 7:32 AM>> create({"a"}as ()->Nothing?)string
>> create({"a"}as ()->Nothing)unit
ephemient
10/24/2021, 7:35 AM() -> Nothing is a () -> Unit, which is because Nothing is a Unit (and also every other type)ephemient
10/24/2021, 7:36 AMNothing? is not a Unit, so the first can't resolve to the more specific `create`(Unit)jimn
10/24/2021, 7:38 AMephemient
10/24/2021, 7:39 AM{ "a" } is not a () -> Nothingephemient
10/24/2021, 7:39 AMjimn
10/24/2021, 7:40 AMephemient
10/24/2021, 7:41 AM({ null } as () -> Unit)() succeeds, even though { null } is a () -> Nothing? hereephemient
10/24/2021, 7:41 AMephemient
10/24/2021, 7:43 AMas doesn't influence the inferred type of its left-hand side expressionjimn
10/24/2021, 7:47 AM>> create({"a"}as ()->Int)string of loader 'bootstrap')
>> create({"a"}as ()->Unit)unit
>> create({1})unit
>> create({1}as ()->Int)string res81: kotlin.Int = 1
jimn
10/24/2021, 7:47 AMjimn
10/24/2021, 7:48 AMephemient
10/24/2021, 7:50 AMfun <S: Unit> create(code: () -> S) = println(code()::class)
create { "a" } // class kotlin.Unit
create({ "a" } as () -> Unit) // class kotlin.Stringjimn
10/24/2021, 7:51 AMephemient
10/24/2021, 7:52 AMephemient
10/24/2021, 7:53 AMas in this way, we are stepping outside of the defined semantics of Kotlinjimn
10/24/2021, 7:54 AMas on {"a"}. we're debating the justification of a billion dollars of software industry rework without blinking.ephemient
10/24/2021, 7:55 AMjimn
10/24/2021, 7:58 AMephemient
10/24/2021, 7:59 AMjimn
10/24/2021, 8:00 AMjimn
10/24/2021, 8:02 AM>> "a"res82: kotlin.String = a
>> {"a"}res83: () -> kotlin.String = () -> kotlin.String
>> create({"a"})
unit
it sounds like you would bet a billion dollars on everyone agreeing with youephemient
10/24/2021, 8:06 AMfoo.also { set.add(it) }
would break without an additional Unit, because add returns a Boolean.ephemient
10/24/2021, 8:07 AM()-returning with just a trailing ;, but that is not Kotlinjimn
10/24/2021, 8:09 AMjimn
10/24/2021, 8:17 AMfoo.also { set.add(it) } I'm completely, honestly fine with Unit<U:Any?-Any?(block:U?){block?.invoke()} in the language to bring about consistency or as a compiler inserted shim for weak platformsephemient
10/24/2021, 8:19 AMfun <T> T.also(block: (T) -> Unit) documents that the return value is not used, whereas your variant doesn'tjimn
10/24/2021, 8:20 AMjimn
10/24/2021, 8:25 AM