"Got to build a path in my code here, so I'll just use 'join' and a backslash... yeah, it isn't portable, but I'm never going to use this app on *nix, so that's ok..."

"Got to run a test here that uses an external file, so I'll just hardcode the path in the .t file... yeah, it isn't portable, but I've never going to move this project anywhere else in the directory tree, so that's ok..."

My meditation: it takes just a bit more extra effort and time to use File::Spec::Function, to make code relocatable, to make code as OS agnostic as possible.

Because inevitably, there's some chance the code will be ported to a different machine, location, IP, OS, whatever. And if you've written it as portably as reasonably possible, it is Sheer Joy when in ports easily. And when you haven't, it is Sheer Annoyance, and you curse yourself for saving a few seconds back then in exchange for hours / days of effort now.

The XP folks wisely warn against designing in extra functionality "just in case" -- I'm not advocating writing piles of extra difficult code just in case you end up porting your module elsewhere, or posting it on CPAN, whatever. I am advocating using simple idioms (like File::Spec) that don't take extra effort and give you the flexibility down the road.

Because inevitably a successful project will wander across different platforms.

Writing from the middle of an XP-to-Linux port --


Replies are listed 'Best First'.
Re: "But I'm never going to..."
by Steve_p (Priest) on Apr 08, 2004 at 03:37 UTC

    water, thank for this meditation. I've been fighting with several modules that I've installed at home on Linux that work just fine, but fail miserably on my Windows box at work because of this very issue.

    I'd just like to add on point though. If you do run into a module that you use on CPAN that fails because of this issue and you take the time to fix it, please take the time to put together a patch for the developer of the module and the public at large. Although it may be a pain to go through this extra step, your helping out the community at whole by making your work available.

Re: "But I'm never going to..."
by Abigail-II (Bishop) on Apr 08, 2004 at 09:06 UTC
    Well, that seems like an open door. But I disagree. Part of programming is finding a trade-off between conflicting goals. Portability is one goal. But readability is another - and not necessarely a less important goal than portability. For the majority of the code I write, if there are any portability issues they are whether they will work with a different version of the kernel, or different versions of other used tools.

    The loss in readability between:

    $file = "fnord/glop/bibba";
    $file = File::Spec -> catfile (qw /fnord glop bibba/);
    is significant. Enough for me to offset the loss of portability to, say, MacOS. Note that according to perlport, fnord/glop/bibba works, or can be made to work, under Unix, Windows, DOS, OS/2, VMS, VOS and RiscOS.
    The XP folks wisely warn against designing in extra functionality "just in case"

    Writing from the middle of an XP-to-Linux port

    I know couple of meanings of XP: 1) a range of disk servers sold by HP, 2) a version of an OS made in the Pacific Northwest, 3) extreme programming. I'm pretty sure you aren't using "XP" in meaning 1). But I can't figure out whether you mean meaning 2) or meaning 3).


      It seemed pretty clear that the first XP was 3) and the second was 2).
      ActiveState perl already understands that slashes and backslashes are equivalent for most modules. This is not to say everything in CPAN will be friendly, but in all of my Windows scripts I just use "/" like normal -- and I haven't had any problems unless I am sending something to the shell.

      In the rare cases where I had to do this, a simple fix_path function is usually sufficient to insert (which turns slashes into backslashes). I *do* need to look at File::Spec and others in these cases, but in general, conciseness is important to me when it can be done without causing problems. And when only dealing with Windows, Linux, and basic Unix systems, this is good enough for me. I'll capitulate when I have to port to something more obscure, I'm sure.

        I've never had a problem with ActiveState not translating between "/" and "\" in a path. The main problem I've had is with the tests, where a path in a string is "t/foo/bar" and Windows returns "t\foo\bar". This is enough to cause a test and CPAN install to fail.

Re: "But I'm never going to..."
by jonadab (Parson) on Apr 08, 2004 at 11:31 UTC

    When I first started doing DBI stuff, I made the mistake of thinking, "I thought out this table design carefully, so I'm never going to add or change any fields." So I hardcoded the fields:

    my $q=$dbh->query("INSERT INTO foo VALUES (?,?,?,?,?); $q->execute($bar, $baz, $qx, $qux, $quux);

    Hah. Don't ever do that. You end up tracking down every single query in your code and fixing it. Multiple times.

    ;$;=sub{$/};@;=map{my($a,$b)=($_,$;);$;=sub{$a.$b->()}} split//,".rekcah lreP rehtona tsuJ";$\=$;[-1]->();print
Re: "But I'm never going to..."
by Jenda (Abbot) on Apr 08, 2004 at 23:24 UTC

    I think one of the reasons File::Spec is not used more is that the default interface is ... erm ...strange. File::Spec->catfile(...)? Beg your pardon?

    Yeah OK so it uses inheritance to allow the default implementations of the functions to be overriden ... but that's just an implementation detail. And implementation details should not leak into the interface.

    The interface provided by File::Spec::Functions is the one that should have been the default one. It looks like the interface of most Perl modules, it allows you to import (or not) functions into your namespace just like any other well behaved module.

    A module that's OO and provides only static methods has no reason to be OO at all. This aint Java.

    Actually it seems to me OO inheritance is not the best implementation. It's slow. There is no reason why the right versions of the functions could not be found just once, on startup. I think it would be better to do something like this:

    #SpecBase.pm package SpecBase; require Exporter; @ISA = qw(Exporter); @EXPORT = @EXPORT_OK = qw(foo bar); sub foo { print "The base foo()\n"; } sub bar { print "The base bar()\n"; } 1; #SpecChild package SpecChild; require Exporter; @ISA = qw(Exporter); @EXPORT = @EXPORT_OK = qw(foo bar); use SpecBase qw(foo); # the inherited functions, to prevent the "Subro +utine bar redefined" warnings sub bar { print "Overwritten bar()\n"; } 1; #Spec.pm package Spec; ... sub import { shift(); ... find out the right version require $the_right_version; $the_right_version->import(@_) } 1;

    Update: Actually looking into the code in File::Spec::Functions I see that the inheritance tree is not being searched through each time. The right method is found by the ->can(). But I think the closure also is not free.

    Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.
       -- Rick Osborne

    Edit by castaway: Closed small tag in signature

      I think one of the reasons File::Spec is not used more is that the default interface is ... erm ...strange. File::Spec->catfile(...)? Beg your pardon?

      That's why Ken Williams, who maintains File::Spec, wrote Path::Class. It's a really, really nice object oriented module for path manipulations. Check out the Synopsis section of its POD to see how much better it is.

      Once it's Turing complete, everything else is just syntactic sugar.