How it comes that Kotlin allows implicit conversions between type aliases. For example:
typealias A1 = (Int) -> Int
typealias A2 = (Int) -> Int
fun main(args: Array<String>) {
val f1: A1 = { it }
val f2: A2 = f1
}
compiles without a warning, but from programmer point of view A1 and A2 are distinct types. On the other hand I cannot assign Int to Long variable:
val i1: Int = 1
val i2: Long = i1 // error
I appreciate Kotlin type safety and I would like it to also extend on type aliases. I guess current behavior is inconsistent with the rest of the language. Am I wrong?
This implicit conversion becomes very useful when it comes to anonymous (lambda) functions.
But I agree with the original author that the problem exits: why we have type safety when we cast interfaces/classes but not for functions? May be there should be/is additional syntax Iâm missing?
Here is an example that shows that type safety is important for classes/interfaces in Kotlin even for «compatible» types:
class C1(x: Int)
class C2(x: Int)
fun fooC1(): C1? = null
fun fooC2(): C2? = null
object O1 {val x: Int = 1}
object O2 {val x: Int = 2}
fun fooO1(): O1? = null
fun fooO2(): O2? = null
interface I1
interface I2
fun fooI1(): I1? = null
fun fooI2(): I2? = null
fun x() {
val c1: C1? = fooC1()
val c2: C2? = fooC1() // Type error!
val o1: O1? = fooO1()
val o2: O2? = fooO1() // Type error!
val i1: I1? = fooI1()
val i2: I2? = fooI1() // Type error!
}
Is there any way to reach the same level of type safety for a top level function declaration?
There is the same level of type safety. Itâs just that (Int) â Int is the same type as (Int) â Int so obviously itâs assignable. In your example code you donât use type aliases. If youâd have used them, they wouldâve been assignable again.
Youâre missing the problem. What you donât like is the behavior of type aliases, not the behavior of function declarations.
This is not true. I like behavior of type aliases in they current form. The goal of my post was to emphasize that there is a disbalance in the language how strong types are supported for classes/objects/interfaces and functions. First have more fine grained type support.
I do not know how to solve this problem (introduce funtype ?) but for a lot of people when they start to use typealiases for functions it becomes a valid question: what is the way to make function conversion explicit? So getOranges will not be occasionally used in the place where getApples is expected.
The traditional name for the feature you expected typealias to be is a âtypedefâ. An âaliasâ, or in this case a âtypealiasâ, works just as the English word does â that is, to the compiler the typealias will actually be the same thing simply called by another name (like a nickname).
(Any issue tracker for a typedef feature open?)
(PS. Actually Iâm lying a bit above⊠Unfortunately the Kotlin compiler doesnât always support typealiases properly, meaning it isnât exactly the same thing as writing out the aliased type )
Although less general than âtypedefsâ, it was mentioned in the recent survey of possible future features that âinline or value classesâ could be used to represent units of measurement, custom strings or dates and even unsigned integers (see item 9 in this list).
As such, they would be distinct from their underlying types without the need for a wrapper object and could have their own properties or methods.
Moreover, as long as this was limited to a single immutable data field, it could be implemented without waiting for value types to be added to Java which is probably several years down the track, if indeed they are ever added at all.
Itâs a feature which I personally would love to see as we could do quite a lot with it and was the fifth most popular (out of 20) with only a few people against.
Its prospects look good because Kotlin Native needs unsigned types for a good C interop story and I doubt whether JB would want to restrict new language features to a single target.
Iâm curious as to why you think that inline classes are less general than typedefs? I was under impression that reverse is true (that is, inline classes are strictly more powerful than typedefs).
Some languages (Nim for example) allow you to create distinct types from any other base type no matter how many fields it has.
If Iâve understood it correctly, under the âinline or value classâ proposal youâll only be able to create a distinct type with a single immutable data field though youâll then be able to split this up into smaller types and expose those as properties.
Incidentally, although I think that when @clubin referred to âtypedefsâ he was talking about distinct types, in the only languages I know (C/C++) where that word is actually used it is, of course, a misnomer. Apart from some minor differences, a typedef is just a type alias (not a type definition) and so, yes, the proposal is more powerful than that.
Disclaimer: we have not committed yet to add any kind of inline classes to Kotlin, so everything I write below is purely hypothetical.
However, Kotlinâs inline clases are designed provide exactly the same features that Nimâs distinct type provides. Nimâs definition of
type
Dollar = distinct int
is semantically equivalent to Kotlinâs:
inline class Dollar(val amount: Int)
In both cases there is no limit on what other type (beyond Int) can be used as a base on which this new type is defined.
P.S. In projects where performance is not of a paramount importance you can already get the functionality of distinct type in Kotlin just by writing as in the example above, but without inline modifier.
Ah, but in the wondrous language Iâm currently involved with that I wonât mention in this forum at the moment, the typedefs are appropriately strong, indeed creating type-system differentiable typesâŠ
Thanks for the reminder about C++'s particularly weak typedef implementation, though, as it seems that Iâve been [mostly] uninvolved with C++ long enough that Iâm finally starting to forget some of the smaller details of exactly how it handled some thingsâŠ
Hypothetically speaking, of course, how would an inline class be represented to the Java side? I would like to write something like this in Kotlin:
inline class Millis(val amount: Long)
@WorkerThread
fun isTimeToEat(db: DatabaseHandle, nowMs: Millis): Boolean {
âŠ
val someLongAsSeconds = db.getMeSomeRawLongsPlease()
// Can't write this due to 'distinct' types!
// return nowMs > someLongAsSeconds
return Long(nowMs) / 1_000 > someLongAsSeconds
}
Would the Java side see a new Millis type or just the plain old Long (or long)? Iâm very interested in this feature, since another useful thing I could do is mark the longs coming from the database as distinct primary key values to avoid mixing them.
Maybe some approach making use of delegates can get something accomplished that is close to what typedefs are, e.g. methods have different signatures and therefore require parameters of different types, but inside those methods the declared different types in the signature do not matter as delegates just forward calls to the âoriginalâ.