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):S
jimn
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 // ClassCastException
jimn
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 () -> Nothing
ephemient
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.String
jimn
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