Sunday 20 August 2023

Returning "nothing"

I've been reviewing a bit how "nullability" and returning "nothing" from functions work in Python, JavaScript and Kotlin.

Python. In Python the null value is represented by the None object. None is an instance (the only instance) of the types.NoneType class, that hence is a singleton.


None.__class__
Out[30]: NoneType

None.__class__.__class__
Out[31]: type

import types

types.NoneType == type(None)
Out[34]: True

None == types.NoneType()
Out[38]: True


Functions without an explicit return statement or with a return without specifying a value, will return None.


# these 3 functions are just the same
def say_hi():
    print("hi")
    

def say_hi_2():
    print("hi")
    return
    

def say_hi_3():
    print("hi")
    return None
    

a = say_hi()
hi

a2 = say_hi_2()
hi

a3 = say_hi_3()
hi

a1 == a2 == a3 == None
a == a2 == a3 == None
Out[46]: True


Javascript. In JavaScript we have 2 different values that we can say mean the absence of value: null and undefined. I could write a whole post about that, so I'll just talk about what this post is about, function returns. A Javascript function without an explicit return statement returns undefined. The same as if you do a return without specifying a value "return;".

Kotlin. In Kotlin functions without a explicit return or with a return without specifying a value, return an instance of Unit. Unit is a singleton class representing a "void return". When declaring a function returning Unit we can skip it in the function signature. The 5 functions below are equivalent, all of them return an instance (the only instance) of Unit


// the "full" way
fun formatAndPrint1(txt: String): Unit {
    println("txt: ${txt}")
    return Unit
}

// the most abbreviated way
fun formatAndPrint2(txt: String) {
    println("txt: ${txt}")
}

// 3 "intermediate" ways
fun formatAndPrint3(txt: String): Unit{
    println("txt: ${txt}")
} 

fun formatAndPrint4(txt: String) {
    println("txt: ${txt}")
    return Unit
}

fun formatAndPrint5(txt: String) {
    println("txt: ${txt}")
    return
}

Kotlin also has the notion of null. The difference between null and Unit is well explained in this discussion:

null and Unit are different things, and meant to be different. null expresses the absence of a value that would usually be there.

fun findByName(name: String): User?

Whereas Unit means essentially “no value”, not even null.

fun delete(item: Item): Unit

There is no useful return value in this case, what is expressed with Unit.

It's a nice distinction that we don't have in Python, where just have None. There's no "equivalence" between Unit and None. A function defined in its signature as returning Unit can not return null, and viceversa, we'll get compiler errors. The 3 functions below cause Compilation errors:


fun sayHi() {
    println("hi")
    return null
}
//compilation error: null can not be a value of a non-null type Unit

fun format2(txt: String): String? {
    if (txt.startsWith("C"))
        return "|${txt}|"
    return Unit
}
// compilation error: type mismatch: inferred type is Unit but String? was expected


fun format3(txt: String): String? {
    if (txt.startsWith("C"))
        return "|${txt}|"
    return 
}
// compilation error: this function must return a value of type String?


No comments:

Post a Comment