Comparison of Type Systems: Kotlin vs Python

After exploring this amazing article, which centers on the Kotlin type system, it’s evident that understanding Type Systems broadly is crucial. While we often equate types with classes, interfaces, and functions, advanced type systems in languages like Kotlin, Python typing, and TypeScript necessitate a broader perspective. This includes grasping concepts like nullable types, union and intersection types (where applicable), Callables-Function Types, and Nothing-Never types. It’s more accurate to view Types as contracts outlining an object’s capabilities – a description of its essence. As stated in the article:

A class is a template for creating objects. A type defines expectations and functionalities.

My previous writings on Top and Bottom types (in TypeScript and Python typing) need clarification, specifically regarding Any as a bottom type. In both articles, my explanation of Any functioning as if it were at the bottom of the Type Hierarchy is not entirely accurate from an academic standpoint. While Any behaves this way in TypeScript and Python, effectively disabling the type-checker, it’s not truly at the bottom. Any allows for the dynamic nature of these languages to coexist with the safety and error prevention of a type system. Because it disables the type-checker, it behaves as if it’s derived from any other type, granting access to any method or attribute without raising type errors. Casting an object to Any allows unrestricted attribute manipulation without type-checker interference.

However, in Kotlin, this “as if” doesn’t apply. Any? (and Any) is genuinely a Top type, not a simulated bottom type. Kotlin’s type-checker cannot be disabled, and its bottom type is Nothing. Casting to Nothing is impossible; it primarily signifies functions that never return (e.g., infinite loops, exceptions). Since Nothing is derived from all other types, a function marked as returning a string can, under certain conditions, return the string or throw an exception without causing compiler errors. Note that instantiating Nothing or casting to it is not allowed. Type theory suggests that bottom types cannot be instantiated, though this is not absolute.

1
2
3
4
 `// can not create instances of Nothing (private constructor)
//var n = Nothing()

println("null is Nothing: ${null is Nothing}") //False`

Kotlin for the JVM presents a unique relationship between Any and Object. In Java, all objects inherit from Object. Conversely, in Kotlin, inheritance originates from Any?, with Object absent from type hierarchy diagrams. While both Any and Object constructors can be used, they both yield an instance of Any at the Kotlin level (verifiable via instance::class). However, at the JVM level, both are instances of Object (as seen with instance::class::java). Casting between Object and Any is possible, but the compiler issues a warning when using Object: “This class shouldn’t be used in Kotlin. Use kotlin.Any instead”.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 `class Animal {}

var any1 = Any()
println("any1::class: ${any1::class}") //kotlin.Any
println("any1::class.java: ${any1::class.java}") //java.lang.Object
println("any1 is Any: ${any1 is Any}") // true
println("any1 is Object: ${any1 is Object}") // true
var ob: Object = any1 as Object

var o1 = Object()
println("o1::class: ${o1::class}") //kotlin.Any
println("o1::class.java: ${o1::class.java}") //java.lang.Object
println("o1 is Object: ${o1 is Object}") // true   
println("o1 is Any: ${o1 is Any}") // true       
any1 = o1 as Any //warning: no cast needed
any1 = o1

var a2: Animal = Animal()

// this does not compile: error: incompatible types: Object and Animal
println("a2 is instance of [Object]?: ${a2 is Object}")
    
// warning: this class shouldn't be used in Kotlin. Use kotlin.Any instead.
o1 = a1 as Object`

In Python, Any acts as both a top type and a pseudo-bottom type. Importantly, Any exists solely at the type-checking level. At runtime, checking for Any instance or creating an instance is not possible. Python’s true bottom type is Never (functionally equivalent to NoReturn).

1
2
3
A special kind of type is Any. A static type checker will treat every type as being compatible with Any and Any as being compatible with every type.[\[1\]](https://docs.python.org/3.12/library/typing.html)

The Any type is both a top type (so you can assign anything to Any) and a bottom type (so you can assign Any to anything). In effect **it turns off the type system** whenever you use Any.[\[2\]](https://discuss.python.org/t/differences-in-bound-object-vs-bound-any/27052/6)

Any exhibits peculiar behavior concerning nullability. Unlike other type declarations in Python, such as a1: Animal (which is non-nullable or, more accurately, not Optional), achieving nullability with Any doesn’t require the typical Optional[Animal] or a1: Animal | None, for Any this is different. Any means any value, including None, so you can assign None to a variable declared as Any (None is compatible with Any), so Python’s Any is different from Kotlin’s Any. Though an Any varible could point to None, the type-checker will not apply on it the restrictions that it applies on nullables (Optional’s), and we can access any attribute on it freely, cause indeed as I aforementioned, “Any disables the type checker”. I we want something equivalent to Kotlin’s Any? we should use Any | None. There’s some discussion about this Any vs Optional[Any] here

We used to simplify any union containing Any to Any, but @ddfisher found out this is incorrect. E.g. if something is either an int or Any, when it is an int, it definitely doesn’t have an append() method.

Licensed under CC BY-NC-SA 4.0
Last updated on Aug 11, 2023 13:06 +0100