listop operator causing infinite recursion, any way to fix?

Viewed 82

I'm looking to possibly help update the File::HomeDir module which was never finished. While inspecting it, I noticed that stubbed out methods were causing infinite loops:

In the File::HomeDir role:

unit class File::HomeDir;

use File::HomeDir::Win32;
use File::HomeDir::MacOSX;
use File::HomeDir::Unix;

my File::HomeDir $singleton;

method new
{
  return $singleton if $singleton.defined;
  
  if $*DISTRO.is-win {
    $singleton = self.bless does File::HomeDir::Win32;
  } elsif $*DISTRO.name.starts-with('macos') {
    $singleton = self.bless does File::HomeDir::MacOSX;
  } else {
    $singleton = self.bless does File::HomeDir::Unix;
  }

  return $singleton;
}

method my-home {
  return File::HomeDir.new.my-home;
}

method my-desktop {
  return File::HomeDir.new.my-desktop;
}

<snip>


In the File::HomeDir::MacOSX module:


use v6;

unit role File::HomeDir::MacOSX;

method my-home {
  # Try HOME on every platform first, because even on Windows, some
  # unix-style utilities rely on the ability to overload HOME.
  return %*ENV<HOME> if %*ENV<HOME>.defined;
  return;
}

method my-desktop {
  !!!
}

<snip>

With this code, calling say File::HomeDir.my-desktop; results in an infinite loop.

This module was first written about 5 1/2 years ago. I'm assuming it worked at the time. But it appears now that if a role method has a listop operator, it causes the parent's class to be called which then called the role method which then calls the parent class, etc.

1 Answers

I'd do it like this, staying close to the original design:

role File::HomeDir::Win32 {
    method my-home()    { dd }
    method my-desktop() { dd }
}

role File::HomeDir::MacOSX {
    method my-home()    { dd }
    method my-desktop() { dd }
}

role File::HomeDir::Unix {
    method my-home()    { dd }
    method my-desktop() { dd }
}

class File::HomeDir {

    my $singleton;
    # Return singleton, make one if there isn't one already
    sub singleton() {
        without $singleton {
            $_ = File::HomeDir but $*DISTRO.is-win
              ?? File::HomeDir::Win32
              !! $*DISTRO.name.starts-with('macos')
                ?? File::HomeDir::MacOSX
                !! File::HomeDir::Unix;
        }
        $singleton
    }

    method my-home()    { singleton.my-home }
    method my-desktop() { singleton.my-desktop }
}

File::HomeDir.my-home;
File::HomeDir.my-desktop;
Related