Overview | Comparisons | Discuss |
Examples | GTK | Injection |
Install | Library | Logging |
Nulls | Plans | Syntax |
Types |
One of my biggest problems with main-stream typed languages is the handling of null. Take this D code (same applies to Java, etc):
class Foo {
void sayHi() {
printf("Hi!\n");
}
}
void main(string[] args) {
Foo foo;
foo.sayHi();
}
If you run this, you get:
$ gdc -Wall -o sayhi sayhi.d $ ./sayhi zsh: segmentation fault (core dumped) ./sayhi
What's the point carefully adding type annotations everywhere if your program's just going to segfault anyway? You might as well save yourself some typing and use Python.
In Delight, object references cannot be null by default. Here's the equivalent:
import dlt.io: Printer
class Foo:
in Printer stdout
void sayHi():
stdout.write("Hi!\n")
class Main:
void main():
Foo foo
foo.sayHi()
It won't compile:
$ delic -o sayhi sayhi.dlt hi.dlt:11: variable hi.Main.main.foo non-null, but missing initialiser
OK, let's initialise it:
class Main:
void main():
Foo foo = null
foo.sayHi()
Nope:
sayhi.dlt:11: Error: cannot implicitly convert expression (null) of type void* to sayhi.Foo
Or even:
class Main:
void main():
Foo foo = new Foo(null)
foo.sayHi()
Nope:
sayhi.dlt:11: Error: cannot implicitly convert expression (null) of type void* to dlt.io.Printer
If you really want a variable that can be null, add ? to the type to make a maybe Foo type. e.g.
class Main:
void main():
Foo? foo = null
foo.sayHi()
The program still won't compile, though:
sayhi.dlt:12: Error: attempt to access property 'sayHi' for 'foo' of type 'Foo?', which may be null.
I first saw this a few years ago in a language called Nice.
You can get false positives in two cases:
The second case isn't a big problem for Delight, because the lack of global state requires wrapping all external libraries anyway.
The first problem can happen in cases like this:
interface AddressBook:
# Returns null if name isn't known
Details? lookup(string name)
...
for name in addressBook:
# Name must be known
Details p = addressBook.lookup(name)
Two general options are:
In this case, a better option would be to use the loop-over-dictionary syntax:
for name, details in addressBook:
...
False-negatives can occur in constructors. You have to assign to all non-null fields in your constructor, but Delight doesn't stop you from accessing them before you've set them (or calling another method which does that).
Note that dynamic arrays (including string) are not pointers in Delight (or D) and therefore cannot be null. An array is a structure containing a pointer and a length. It is passed by value (though the pointer is shared).
An empty array may or may not contain a null pointer, depending on how it was allocated, but Delight (unlike D) treats them the same either way.
D allows you to use null for an empty array, which is rather confusing. Delight only accepts [] for this.
You can turn a maybe type into a regular non-null type using an if statement:
if Details details = addressBook.lookup(name):
return details.extractData()
else:
throw new Exception("Name " ~ name ~ " not known!")
When assigning the result of the condition, the variable can be declared non-null, even if the condition has a maybe type. This is because the new variable (details) is only in scope in the "then" part of the statement, where it cannot be null (because null is false).
Overview | Comparisons | Discuss |
Examples | GTK | Injection |
Install | Library | Logging |
Nulls | Plans | Syntax |
Types |