To java programmers, ‘Null Safety’ can sound like the ‘Holy Grail’. To python programmers, ‘Null Safety’ can sound like solving a problem they never had.
So what is ‘null’ safety? Firstly, if you any questions on what ‘null’ is, then check the page None, Unit, Nothing, void and null.
Null safety is all about preventing code from accidentally using a value which is null.
Null is normally a placeholder for a value, only used where the value is not yet known. A variable ‘foo’ which is to be an ‘int’ could be set to ‘null’ until an actual value is calculated and set. But what if there is a case when the program accidentally does not set ‘foo’? If you try and add the python equivalent of ‘null’ (None) to an int value, the error “TypeError: unsupported operand type(s) for +: ‘int’ and ‘NoneType'” will be raised. Whoopsy! Null safety is the language ensuring you never make the mistake of forgetting to check for ‘null’ when you need to check.
Truth is, the ‘null’ (or None) problem is still there for python programs. The problem will arise in a different way (most often ‘NoneType object has no attribute…’, or a TypeError as above) but the lack of a true equivalent to Null excludes Python code from a very elegant solution to handling special cases, and forces more exception handling. Python has no ‘null’ (see Nullable, null, None, …) and the Python ‘None’ is more accurately an equivalent to the kotlin ‘Unit’.
- what is null? (see Nulllable, null, None…)
- background: consider python with types
- the use of null safety
- null safety syntax
- what exactly is the safety?
- the impact on programming
Background: consider Python with types
Consider the python code in Python Variables, where the first line reads “a: int”. What this code does is, at best, a little strange. It says “the variable called ‘a’ will be an int, but does not yet exist”. How can an ‘int’ have type noted, but not actually yet exist? The reality is in python we could set our variable to ‘None’ to indicate it has no value, and then the variable would actually exist, but then ‘None’ is not an int ‘type(None)’. So to set this new variable to ‘None’ in python, it needs to a be “int or None” and to write that in python (which is very new for python) we would need “a: Union[int, None]”. Far easier to simply give a value to our new variable, as the code does with ‘b’. Line 2, “b:int = 6” declares ‘b’ and sets ‘b’ to 6. Simple in both languages. So now we have learnt the following lines are all equivalent:
from typing import Union a = None # python, the usual way! a: Union[int, None] = None #python with types var a: Int? = null// kotlin
The first example (line 2) is typical python, but gives no indication to the language, or to a person reading the program, about the actual use of ‘a’. The example (line 3) is python using types, (as technically could be a:Optional[int] – but the union is perhaps more self explanatory), and the third option is kotlin. In kotlin, “Int?” is saying the python “either int or None”, but in kotlin. The kotlin code is nice, but almost all current python code would simply set the variable ‘a’ to ‘None’, and not bother declaring the types. In python, normally the programmer just has to remember “I am using ‘a’ as int, but it could be a None”. The programmer themselves has to remember ‘a’ could be None.
So far, kotlin has made a nicer way to say something, that most python code does not bother to say anyway. Yes, it is a good idea to document in some way that this ‘a’ should be an ‘int’, but beware it can also be None, and the kotlin is probably a better way to document what is happening, but how does this help?
The Use of Null Safety
Null safety is that not only have we documented that our variable can be “None” or “null”, but that the language can help us avoid getting errors. Kotlin does this by making it super easy to check for ‘null’, and forces us to check for null anywhere null is a possible value. This can avoid many a program error.
Consider a diagram similar to the background diagram of variables. In kotlin, ‘a’ is pointing nowhere, while in python ‘a’ would be pointing to the ‘None’ object. But in either case, if we try and use ‘a’ as an integer, we will get an exception.
We could, for example, wish to print a variable ‘a’ if it is not null, and print zero for null. As a second and better example, we may wish to print ‘a+1’, but print None or null for any case where a is ‘None’ (python) or ‘null’ in kotlin. In this second case adding to None/null would give an exception if we forget to check. The code would be:
print(a if a is not None else 0) # puts what we are printing first ...messy! print(a+1 if a is not None else a) # example 2 in python # or .... print(0 if a is None else a) # example 1:shorter python -but hard to read print(a if a is None else a+1) # example 2: shorter python again println(a ?: 0) // example 1: kotlin println(a?.add(1)) // example 2: kotlin
The kotlin code is short for example 1, and more importantly, for example two where there would be an error if we try to add 1 to null, kotlin forces the check. The syntax is explained below.
Even the shorter python version is longer than the kotlin code, and neither python version is ideal. The shorter version hides that we are normally printing (‘a’) and the longer version has a messy ‘not None else’ but still tells the story better than the shorter code. But the main point is that if we forget that ‘a’ could be ‘None’ for example 2, then this is only discovered in testing, and then only if we remember to test that case. Kotlin reminds us automatically that ‘a’ could be null, and ensures the code encounters no problem.
One more example:
a = None # for python 3.6 could use "a:Union(str, None) = None" .... (some code that might set a, but might leave not set) print(0 if a is None else len(a)) # python a: String? = null ......(some code that might set a to a new string) println(a?.length ?: 0) // kotlin
Again kotlin will ensure we deal with the possibility that ‘a’ is null and will prevent code that could break at run time, plus it makes that code very easy to write.
The end result is that it becomes far easier, and safer, to code solutions where ‘null’ represents ‘has no real value at this time’. An important technique becomes easier to use, and safe when it is used.
Null Safety Syntax
The first part of the syntax is that Kotlin needs to know about values being nullable. Nullable values can come from a variable, or be calculated by a function. A variable cannot be set to null, or to the result of a nullable function, unless the variable has nullable type, so the checking of null being allowed is automatic. A Kotlin function cannot return a nullable value unless the function is declared as having a nullable return value, so again the checking is also automatic. The only source of the unexpected nulls is from functions declared from external Java or C (or some other language) which can incorrectly report if they may return null. Note that some Java or ‘C’ functions can be declared with ! types, such as Int! in place of Int? This means it is not known if they may return null or not. For safety, you can treat these as with ?.
Any time the Kotlin compiler sees a nullable value, the compiler itself will produce an error unless you code explicitly handles the possibility of null.
Null operations, to handle ‘nullable’ values.
//syntax 1 - the if val value: Int? = null if(value != null){ // kotlin automatically detects that 'value' cannot be null // inside this if statement } // syntax 2 … the ?. val value: Int? = null value.toString() // this will produce and error as value could be null value?.toString() // this will work, but the result is also nullable // the ?. does not call the function or return get the property, it instead // simple returns null if value is null. So .toString() is not called when // value is null //syntax 3 … the ?: (also known as the elvis operator) val value1: Int? = null val value2: Int = value1 ?: 0 val value2 = if(value1 != null) value1 else 0 //same as above //syntax 4 … the !! or 'die' operator val value1: Int? = 3 val value2: Int = value1!!
syntax 1: If statements, and also when statements, can tell the compiler that code will or will not be reached when there is a possibility of a value being null. The compiler ‘smartcasts’ Int? to Int (or any nullable to non-nullable) automatically within if or when blocks.
syntax 2: The ?. operator replaces the ‘.’ operator to access properties or methods of nullable objects. The ‘.’ returns the attribute of the object, while the ‘?.’ returns null if the value is null, and the property of the object when not null. This means the result of ?. will itself be possibly a null and thus nullable.
syntax 3: The ?: or elvis operator. This operator takes a nullable expression to the left and a second expression to the right. The expression on right must be the same type as the expression on the left, without being nullable. This operator works exactly the same as an if, where the result is the left-expression if that is not null, else the value is the right expression.
Syntax 4: !! unsafe. This ‘!!’ operator turns off null safety, and terminates the program if a null value is detected. Use this only if it makes no sense for the program to continue if a null value is present.
What exactly is the safety of null safety?
Null safety is simply that all these rules ensure there are only two ways to get the dreaded null pointer exception. The null pointer exception is generated when you do anything else with ‘null’ other than check that it is ‘null’. The only two ways to still do something with null by accident are:
- code external to Kotlin that is declared to be not nullable, but actually does return null
- use of the !! operator
The impact of Null Safety
Null safety has much greater impact that simply eliminating null pointer exceptions. With concise syntax, and special features to check for null values, using null values to indicate special status become a key programming tool. As a programming tool, returning a null value becomes a preferred way to indicate a special case, and can eliminate the need to generate and catch exceptions.
Kotlin is free to use this special power of null. Examples like the ‘toIntorNull’ function to convert a string to Integer and indicate failure by returning a null value is one example. But in many cases where python would generate an exception, Kotlin can simply return a null.
string = input("Number please(default is zero)? ") try: number = int(string) except ValueError: number = 0 # dictionary key rgb = { "red":1, "green":2, "blue" } answer = rgb.get(input("rgb: "), 0)
Using rgb[string] will generate an exception in python if ‘string’ is not a valid key, but the get method (also available in Kotlin) does allow for eliminating an exception in this case.
Now Kotlin:
print("Number please(default is zero)? ") val number = readLine()?.toIntOrNull() ?: 0 # dictionary key rgb = { "red":1, "green":2, "blue" } print("rgb: ") answer = rgb[readLine()] ?: 0
In summary, null safety allows changing from having expected exceptions to code that directly handles the possible cases and relegating exceptions to being actually exceptional.
I really loved how Kotlin offers compiler checks for possible Null cases. That said, I haven’t had too many issues with a None reference in my Python code.
http://www.stonesoupprogramming.com
LikeLiked by 1 person
Unlike a ‘null’, a None reference is not a problem. With python’s ‘duck typing’ the problem comes when you try to use the ‘None’ object in some way, for example:
>>> a + b
TypeError: unsupported operand type(s) for +: ‘int’ and ‘NoneType
of with a callback:
‘NoneType’ object is not callable
So the error will mention the NoneType, but the error is not because something is None, but because ‘None’ cannot do what the real object would do.
P.S. Your coding instructions for python looks good
LikeLike
[…] null safety […]
LikeLike
[…] Null Safety […]
LikeLike