I'm trying to create a class 'Gprogram' that satisfies the interface Iterable
(such that I can iterate over the Gcommand's in my Gprogram). However, I can only make it iterable with the type Iterable<Gcommand|Null,Nothing>
where I would rather have Iterable<Gcommand,Nothing>
.
The problem is: when I try to use Iterable<Gcommand,Nothing>
, I get this error:
specified expression must be assignable to declared type of 'next' of 'Iterator' with strict null checking: 'Null|Gcommand|finished' is not assignable to 'Gcommand|Finished' (the assigned type contains 'null')
this error refers to this code snippet:
next() => index>=size
then finished
else gcommands[index++];
which is taken from the full implementation here:
shared class Gprogram( Gcommand+ gcommands ) satisfies Iterable<Gcommand|Null,Nothing> {
shared actual default Iterator<Gcommand|Null> iterator() {
if (gcommands.size > 0) {
return object
satisfies Iterator<Gcommand|Null> {
variable Integer index = 0;
value size = gcommands.size;
next() => index>=size
then finished
else gcommands[index++];
string => gcommands.string + ".iterator()";
};
}
else {
return emptyIterator;
}
}
}
The issue seems to me to be that the type checker can't realize that the next
method can't ever return null (realising so would involve reasoning about integer values, which the type checker can't do). Thus, all hope is out, right..?
One nagging question remains: How does List
manage to do what I can't?? Let's have a look at the implementation of iterator
for the List
class:
shared actual default Iterator<Element> iterator() {
if (size>0) {
return object
satisfies Iterator<Element> {
variable Integer index = 0;
value size = outer.size;
next() => index>=size
then finished
else getElement(index++);
string => outer.string + ".iterator()";
};
}
else {
return emptyIterator;
}
}
where the getElement
function looks like this:
Element getElement(Integer index) {
if (exists element = getFromFirst(index)) {
return element;
}
else {
assert (is Element null);
return null;
}
}
( full source code )
It can be seen that getElement
is very much capable of returning Null values. But how can it then be that List
's satisfaction of the Iterable
interface doesn't mention Nulls, like my implementation is forced to do? The relevant 'satisfies'-statement resides in List
's supertype, Collection
. See here:
shared interface Collection<out Element=Anything>
satisfies {Element*} {
( full source code )
Look, ma! No {Element|Null*}
!