Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to keep Nil from reverting container to its default value?

I'm implementing a simple linked list and to denote the fact that there is no next node, I'm using the value Nil. The thing is that when assigned to a container, Nil will attempt to revert the container to its default value which means I need to use the container's default value or Any to determine if the linked list's end has been reached. However, I'd still like to use Nil (if only for its clear intent) and no other value to test for the non-existence of a next node in the code (e.g., until $current === Any, last if $current-node === Any;). For some context:

class Node {
    has $.data is rw is required;
    has $.next is rw = Nil;
}

class UnorderedList {
    has $!head = Nil;
    
    method add(\item --> Nil) {
        my $temp = Node.new: data => item;
        $temp.next = $!head;
        $!head = $temp;
    }

    method size(--> UInt) {
        my $current = $!head;
        my UInt $count = 0;
        
        until $current === Any {          # <=== HERE
            $count += 1;
            $current .= next;
        }
        
        return $count;
    }
   
    method gist(--> Str) {
        my $current-node = $!head;
        my $items := gather loop {
            last if $current-node === Any; # <=== HERE
            take $current-node.data;
            $current-node .= next;
        }
        
        $items.join(', ')
    }
}
like image 310
Luis F. Uceta Avatar asked Jul 05 '20 14:07

Luis F. Uceta


1 Answers

This is a bit like asking "how can I still have rain fall on my head while using an umbrella". :-) The primary reason Nil exists in Raku is to provide a value that a function can return on a soft failure to produce a result, which will be safe if assigned into any container that supports being undefined (or defaulted).

Thus, one can write a function func that can return Nil, and it will Just Work for a caller my SomeType $foo = func() or for a caller my OtherType $bar = func().

While there is the is default(Nil) trick, I'd strongly suggest using some other value as your sentinel. Trying to use a language feature in a situation where you don't want the primary behavior it exists to provide will generally not go smoothly.

The Node type object itself would be a reasonable choice. Thus this:

has $!head = Nil;

Becomes:

has Node $!head;

And the test becomes:

until $current === Node {

However, I'd probably write it as:

while $current.defined {

Which also supports subclassing of Node. On the other hand, if I know Node is an implementation detail of my class, I'd feel safe enough to use rely on the default boolification semantics to remove the clutter:

while $current {

Meanwhile, this:

last if $current-node === Any;

Could become:

last without $current-node;

Or for consistency if choosing the "rely on the boolification" approach:

last unless $current-node;

Finally, if Node is just an implementation detail, I'd move it inside UnorderedList and make it my scoped.

like image 52
Jonathan Worthington Avatar answered Oct 03 '22 04:10

Jonathan Worthington