June 05, 2013
currently there's no way to perform cross-platform operations.

what about:
---
enum Platform{Posix,Windows}
version(Posix) enum PlatformDefault=Platform.Posix; else enum
PlatformDefault=Platform.Windows;
struct Path(T=PlatformDefault){}

void main(){
Path!(Platform.Posix) path="a\b";
auto path2=path.to!Path;
}
---

it allows current usage with no modification, and allows cross-platform logic.


On Wed, Jun 5, 2013 at 1:19 PM, Dylan Knutson <tcdknutson@gmail.com> wrote:

> I don't have a strong opinion regarding Path object vs. string functions,
>> and I agree both have advantages and disadvantages. But I would be opposed to having both.
>>
>
> Personally, I'd prefer to just use the Path struct in std.path. While a Path can be represented as a string, it's not the same concept (the same way that a DateTime can be represented as an integer, but we don't just pass times around as raw integer types).
>
> However, I can't imagine that'd be feasible as it'd break a lot of code. My suggestion would be to just keep the freestanding functions to operate on raw strings, and then migrate over code that depends on std.path to use the Path struct as needed. I realize that this is easier said than done, but even then it shouldn't be a lot of work as Path can implicitly be converted to a string.
>
> This makes its integration into existing codebases/Phobos literally as easy as using "Path my_path = `foo\bar`" instead of "string my_path = `foo\bar`". You lose no functionality but gain type safety if you have to do any future refactoring.
>
>
>   I wouldn't like to create an "object" just to call isAbsolute.
>>
>
> Agreed. The best course of action would probably be keep the raw functions as they exist (think of them as the static versions of Path methods). However, for large applications, the type safety of a Path object makes working with paths much easier.
>


June 05, 2013
On Wednesday, June 05, 2013 22:19:21 Dylan Knutson wrote:
> > I don't have a strong opinion regarding Path object vs. string functions, and I agree both have advantages and disadvantages. But I would be opposed to having both.
> 
> Personally, I'd prefer to just use the Path struct in std.path. While a Path can be represented as a string, it's not the same concept (the same way that a DateTime can be represented as an integer, but we don't just pass times around as raw integer types).

There's a significant difference between a type which has a value and units and one which is basically just a string or array of strings wrapped by another type. Not that a Path struct is without value, but I think that there's a very large difference in the amount of value that the two provide. AFAIK, very few bugs are caused by treating paths as strings, but there are a lot of time- related bugs out there caused by using naked values instead of values with units.

> This makes its integration into existing codebases/Phobos literally as easy as
[snip]

See, this is exactly the sort of thing I'm afraid of. I don't want to have to have arguments over whether a particular function should accept a path as a string or a struct. Right now, we have one way do to it, so it's clear, and it works just fine. If we add a Path struct, then we have two ways to do the same thing, and we're going to have a division among APIs as to which way to handle paths. And I think that we'll be very much worse of because of it. While there is value in having a path struct rather than a string, I don't think that it's worth the extra confusion and division that it'll cause. If we were going to have a path struct, we should have done that in the first place rather than using strings.

- Jonathan M Davis
June 05, 2013
On Wed, Jun 5, 2013 at 1:34 PM, Jonathan M Davis <jmdavisProg@gmx.com>wrote:

> On Wednesday, June 05, 2013 22:19:21 Dylan Knutson wrote:
> > > I don't have a strong opinion regarding Path object vs. string functions, and I agree both have advantages and disadvantages. But I would be opposed to having both.
> >
> > Personally, I'd prefer to just use the Path struct in std.path. While a Path can be represented as a string, it's not the same concept (the same way that a DateTime can be represented as an integer, but we don't just pass times around as raw integer types).
>
> There's a significant difference between a type which has a value and
> units and
> one which is basically just a string or array of strings wrapped by another
> type. Not that a Path struct is without value, but I think that there's a
> very
> large difference in the amount of value that the two provide. AFAIK, very
> few
> bugs are caused by treating paths as strings,


I disagree.

It allows to catch bugs early (eg: giving a $mypath environment variable to a binary, where the env variable wasn't set or set to an invalid path name). Constructing a Path object from it will immediately fail as opposed to later down the code with possibly evil artifacts (eg when using std.process.shell functions).

Other advantage : central location for all path object creations allows to instrument the code for example for logging all path names mentioned. Would be impossible with raw string type.

Other advantage: makes it easy to work with cross-platform code (ie operating on windows paths from posix), see my previous post in this thread.

I very much welcome this. There's a reason why other languages (C#, java) have such an abstraction. Given D's alias this functionality, this abstraction comes at 0 runtime cost and makes it work with 0 adaptation for most existing code.

What will it break? We should discuss that.



> but there are a lot of time-
> related bugs out there caused by using naked values instead of values with
> units.
>
> > This makes its integration into existing codebases/Phobos literally as easy as
> [snip]
>
> See, this is exactly the sort of thing I'm afraid of. I don't want to have
> to
> have arguments over whether a particular function should accept a path as a
> string or a struct. Right now, we have one way do to it, so it's clear,
> and it
> works just fine. If we add a Path struct, then we have two ways to do the
> same
> thing, and we're going to have a division among APIs as to which way to
> handle
> paths. And I think that we'll be very much worse of because of it. While
> there
> is value in having a path struct rather than a string, I don't think that
> it's
> worth the extra confusion and division that it'll cause. If we were going
> to
> have a path struct, we should have done that in the first place rather than
> using strings.
>
> - Jonathan M Davis
>


June 05, 2013
> There's a significant difference between a type which has a value and units and
> one which is basically just a string or array of strings wrapped by another
> type. Not that a Path struct is without value, but I think that there's a very
> large difference in the amount of value that the two provide. AFAIK, very few
> bugs are caused by treating paths as strings, but there are a lot of time-
> related bugs out there caused by using naked values instead of values with
> units.

Dub is forced to define its own separate Path type because, as its author states, using a string to represent a path "more often than not results in hidden bugs." (https://github.com/rejectedsoftware/dub/issues/79). Representing a path is just fine in a small script, but the moment you've got to handle stuff like path comparison, building, and general manipulation, you're better off defining an abstraction for it.

> See, this is exactly the sort of thing I'm afraid of. I don't want to have to
> have arguments over whether a particular function should accept a path as a
> string or a struct. Right now, we have one way do to it, so it's clear, and it
> works just fine.

I see no problem with just keeping Phobos as it is, it was just a suggestion to make use of new functionality. A function that takes a string can accept a Path *or* a string, and it'll work just fine, thanks to subtyping.

    void bar(Path path) { return; }
    void foo(string str) { return; }

    Path p = `baz\quixx`;

    bar(p);
    foo(p);

So there doesn't have to be an argument over what a function should accept; that's up to the function's internal implementation. From the outside, it'll accept both.

June 05, 2013
On Wednesday, 5 June 2013 at 06:27:46 UTC, Dylan Knutson wrote:
> Hello,
> I'd like to open up the idea of Path being an object in std.path. I've submitted a pull (https://github.com/D-Programming-Language/phobos/pull/1333) that adds a Path struct to std.path, "which exposes a much more palatable interface to path string manipulation".

For the record, there are some existing D path object implementations:

* Tango's FilePath class:
  https://github.com/SiegeLord/Tango-D2/blob/d2port/tango/io/FilePath.d

* Vibe's Path struct:
  https://github.com/rejectedsoftware/vibe.d/blob/master/source/vibe/inet/path.d
June 05, 2013
On Wednesday, 5 June 2013 at 22:06:52 UTC, Vladimir Panteleev wrote:
> On Wednesday, 5 June 2013 at 06:27:46 UTC, Dylan Knutson wrote:
>> Hello,
>> I'd like to open up the idea of Path being an object in std.path. I've submitted a pull (https://github.com/D-Programming-Language/phobos/pull/1333) that adds a Path struct to std.path, "which exposes a much more palatable interface to path string manipulation".
>
> For the record, there are some existing D path object implementations:
>
> * Tango's FilePath class:
>   https://github.com/SiegeLord/Tango-D2/blob/d2port/tango/io/FilePath.d
>
> * Vibe's Path struct:
>   https://github.com/rejectedsoftware/vibe.d/blob/master/source/vibe/inet/path.d

The design of Path was prompted by Dub's own internal path module, might I add. And if anything, this just goes to show that a Path object indeed does have its use cases.
June 05, 2013
I'd like to point out some of the pitfalls of using a raw string
as a representation of a path, too.

You've got to manually normalize strings before any comparison is
done. Even a single directory delimer at the end of the string
means that the paths won't compare correctly. This takes a good
amount of extra code to do so, and you've got to remember to
normalize *everywhere*, or you've got a bug waiting to happen.
string s1 = `baz/../foo/bar/`;
string s2 = `foo/bar/`;
string s3 = `foo/bar`;

assert(s1 == s2); // Fails
assert(s2 == s3); // Fails
assert(s1 == s3); // Fails
assert(buildNormalizedPath(s1) == buildNormalizedPath(s2)); //
Passes, with many more keystrokes.

Comparing with Paths:
Path p1 = `baz/../foo/bar/`;
Path p2 = `foo/bar/`;
Path p2 = `foo/bar`;

assert(p1 == p2); // Passes.
assert(p2 == p3); // Passes.
assert(p1 == p3); // Passes.

As you can see, Path is just generally easier to work with,
because it encapsulates the concept a path. There's no having to
normalize strings, because that's done for you. It just works.

Building a path with strings isn't difficult, but the function
calls are unweildy.
string s1 = buildNormalizedPath(`foo`, `bar`);
string s2 = buildNormalizedPath(s1, `baz`);
assert(s2 == `foo/bar/baz`); // Will fail on some platforms.

Building a Path, IMO, just looks cleaner, and it's obvious what
you're doing.
Path p1 = Path(`foo`, `bar`);
Path p2 = p1.join(`baz`);
assert(p2 == `foo/bar/baz`); // Passes on all platforms.

As a sidenote, I'd like to point out that using Path has *no more
overhead* than passing around and manipulating a raw string.
As far as I can tell, all use cases for Path takes less code, and
more easily convays what you're doing. D's support for object
oriented design is great; why not make use of it?
June 06, 2013
On Wednesday, 5 June 2013 at 20:52:24 UTC, Dylan Knutson wrote:
> Dub is forced to define its own separate Path type because, as its author states, using a string to represent a path "more often than not results in hidden bugs."

You're miss quoting here. "usually will be places where the path is modified using string operations [...]"

While I've had desires to have my functions accept a Path so that I can document what is being accepted (also helps with function overloads), std.path has been working well for me as I move my code from string operations to path operations.
June 06, 2013
On Wednesday, 5 June 2013 at 22:06:52 UTC, Vladimir Panteleev wrote:
> * Tango's FilePath class:
>   https://github.com/SiegeLord/Tango-D2/blob/d2port/tango/io/FilePath.d

Note that Tango code should not be used for code intended for Phobos unless all authors of that piece have stated they will license under Boost. It is a firm stance to prevent any potential legal issues (whether perceived or real)
June 06, 2013
On Wednesday, 5 June 2013 at 06:27:46 UTC, Dylan Knutson wrote:
> Hello,
> I'd like to open up the idea of Path being an object in std.path. I've submitted a pull (https://github.com/D-Programming-Language/phobos/pull/1333) that adds a Path struct to std.path, "which exposes a much more palatable interface to path string manipulation".

Since I am the designer and primary author of std.path, I should probably say something.

When I first started working on "the new std.path" a couple of years ago, I initially entertained the idea of writing it in terms of a dedicated Path type.  I was quickly convinced otherwise by others, and proceeded to design the module around normal strings.

For the last two years I've been working more in C++ than in D (by necessity, not by desire), and for all my path-manipulation needs I've been using boost::filesystem.  This library has a dedicated path type, so I've gained some experience with this kind of API.  And I am *really* happy we went with the string solution for std.path.

Paths are usually obtained in string form, and they are normally passed to other functions and third party libraries in string form.  Having to convert them to something else just to do what is, in fact, string manipulations, is just annoying.

(One of my biggest gripes with boost::filesystem is that conversions between path and string necessitate a copy, which is not a problem with your Path type, so in that respect it is better than Boost's solution.)


> [...]
>
> Why I think it should be reconsidered for inclusion in the std (listed in the pull):
> * Adds a (more) platform independent abstraction for path strings.

How is this more platform independent?  It is just a simple wrapper around a string, with methods that forward to already-extant module-level functions.


> * Path provides a type safe way to pass, compare, and manipulate arbitrary path strings.

How is it safer?  I would agree with this if it verified that isValidpath(_path) on construction and maintained this as an invariant, but I cannot see that it does.


> * It wraps over the functions defined in std.path, so behavior of methods on Path are, in most cases, identical to their corresponding module function.

Then what is the added value?

Having Path together with normal string functions in the same module will be confusing (there are two almost-equal ways of doing the same thing; which one should I choose?), and it will add code duplication (now my code has to accept paths both as strings and as Paths).

As the author of std.path this may come across as hostile or jealous, but I don't see that the proposed change improves anything.

Lars