ratfactor/ziglings

Exercise 48: Bad optional style?

chancyk opened this issue · 9 comments

https://github.com/ratfactor/ziglings/blob/main/exercises/048_methods2.zig#L13

// Elephant tail methods!
pub fn getTail(self: *Elephant) *Elephant {
    return self.tail.?; // Remember, this means "orelse unreachable"
}

pub fn hasTail(self: *Elephant) bool {
    return (self.tail != null);
}

Does this not defeat the point of optional types? In that if I forget to call hasTail, the compiler can't help me catch that I've forgotten self.tail can be null since we've overridden the optional behavior using .??

Do you mean something like this

if (e.tail) |tail| {
    e = tail;
} else {
     break;
}

would be better?

@chrboesch I think so. The getTail implementation panics at runtime versus the compiler catching the error if you were to just e = e.tail without the check in your example.

Using e = e.getTail();:

C:\Projects\demos\zigdemos\ziglings\exercises (main -> origin)                                                                        
λ zig run 048_methods2.zig                                                                                                            
A  B  C  thread 18468 panic: attempt to use null value                                                                                
C:/Projects/demos/zigdemos/ziglings/exercises/048_methods2.zig:14:9: 0x7ff7d7962266 in getTail (048_methods2.exe.obj)                 
        return self.tail.?; // Remember, this means "orelse unreachable"                                                              
        ^                                                                                                                             
C:/Projects/demos/zigdemos/ziglings/exercises/048_methods2.zig:56:22: 0x7ff7d7961878 in visitElephants (048_methods2.exe.obj)         
        e = e.getTail();                                                                                                              
                     ^                                                                                                                
C:/Projects/demos/zigdemos/ziglings/exercises/048_methods2.zig:41:19: 0x7ff7d7961355 in main (048_methods2.exe.obj)                   
    visitElephants(&elephantA);                                                                                                       
                  ^                                                                                                                   
C:\Bin\zig-windows-x86_64-0.10.0\lib\std\start.zig:377:41: 0x7ff7d7961297 in WinStartup (048_methods2.exe.obj)                        
    std.debug.maybeEnableSegfaultHandler();                                                                                           
                                        ^                                                                                             
???:?:?: 0x7ffd93fb26bc in ??? (???)                                                                                                  
???:?:?: 0x7ffd94fadfb7 in ??? (???)                                                                                                  

Using e = e.tail;:

C:\Projects\demos\zigdemos\ziglings\exercises (main -> origin)                                                                        
λ zig run 048_methods2.zig                                                                                                            
048_methods2.zig:56:14: error: expected type '*048_methods2.Elephant', found '?*048_methods2.Elephant'                                
        e = e.tail;                                                                                                                   
            ~^~~~~                                                                                                                    
048_methods2.zig:56:14: note: cannot convert optional to payload type                                                                 
048_methods2.zig:56:14: note: consider using `.?`, `orelse`, or `if`                                                                  
048_methods2.zig:56:14: note: '?*048_methods2.Elephant' could have null values which are illegal in type '*048_methods2.Elephant'     
referenced by:                                                                                                                        
    main: 048_methods2.zig:41:5                                                                                                       
    callMain: C:\Bin\zig-windows-x86_64-0.10.0\lib\std\start.zig:596:17                                                               
    remaining reference traces hidden; use '-freference-trace' to see all reference traces                                                                                                                                                                     

In this case, my suggestion would look like this:

// New Elephant methods!
pub fn getTail(self: *Elephant) ?*Elephant {
    return self.tail; // Remember, this means "orelse unreachable"
}
if (e.getTail()) |tail| {
    e = tail;
} else {
    break;
}

I withdraw the proposal because, while technically correct, it distorts the meaning of the methods. There is still something missing in the considerations.

I think you could stay align with the previous optional2 exercise with your previous example:

    pub fn getTail(self: *Elephant) ?*Elephant {
        return self.tail;
    }

But reduce the while loop by :

    while (true) {
        e.print();
        e.visit();
        e = e.getTail() orelse break; // Which method do we want here?
    }

It's just an another suggestion...
And also it can be align with the quiz6.

A nice suggestion, but it won't work. But this would:

    while (true) {
        e.print();
        e.visit();

        // This gets the next elephant or stops.
        e = if (e.hasTail()) e.??? else break;  // Which method do we want here?
    }

Sry miss type e = e.tail orelse break become e = e.getTail() orelse break.
and so the hasTail is no more used.
It build successfully.
Did i miss something ?

When I try e = e.getTail() orelse break I get error: expected optional type, found.
But before we start looking for a smoother solution, let us keep in mind what is the exercise for: "methods".
We should try not to eliminate all methods for a nicer look. Of course, there are better solutions, but then can we still convey the theme?

I think it will not get much better. But if someone has a better solution, we can open this issue again.