# Better handling of pathnames

**URL:** <https://discourse.julialang.org/t/better-handling-of-pathnames/36792>\
**Category:** Community\
**Tags:** question, package, filesystem\
**Created:** [March 31, 2020, 10:16am UTC](https://discourse.julialang.org/t/better-handling-of-pathnames/36792 "2020-03-31T10:16:35Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![jessymilare](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jessymilare/32/13750_2.png) [@jessymilare](https://discourse.julialang.org/u/jessymilare)\
**Post date:** [March 31, 2020, 10:16am UTC](https://discourse.julialang.org/t/better-handling-of-pathnames/36792/1 "2020-03-31T10:16:35Z")

</div>

I’m new to Julia, loving it. As I’m used to Common Lisp, I kinda miss a pathname structure. Of course I understand strings are easier to deal with, yet they lead to mistakes. The package [FilePaths.jl](https://github.com/rofinn/FilePaths.jl) is nice, yet it is weird to have to call `string(path)` when using other packages like OdsIO or CSV. I think it’s not a nice practice to define methods belonging to another one’s package for another one’s classes (e.g. `csv_read(file::Path; kwargs...) = CSV.read(string(file); kwargs...)`), since a third person could redefine them another way.

One approach would be to create methods for functions that expects strings, e.g.

```julia
csv_read(file::AbstractPath; kwargs...) =
    CSV.read(string(file); kwargs...)
csv_write(file::AbstractPath, table; kwargs...) =
    CSV.write(string(file), table; kwargs...)
# etc

```

I don’t like that very much.

For that reason, I was thinking about creating a new package with pathnames as a subtype of `AbstractString`, specifying a unique string representation for each pathname (like TCL does, what a nice language!). That way, one deal with pathnames as structures and use them in methods that expects strings. Plus, it would be ease to extend this interface to, e.g., URLs, sockets, etc., and having them to function appropriately.

What do you guys/gals think?

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [March 31, 2020, 10:58am UTC](https://discourse.julialang.org/t/better-handling-of-pathnames/36792/2 "2020-03-31T10:58:55Z")

</div>

> [@jessymilare](#):
>
> used to Common Lisp, I kinda miss a pathname structure

I feel the same way occasionally, even though I was complaining about it being a bit baroque the time I was using CL. I guess that’s karma for me 😉

> [@jessymilare](#):
>
> a new package with pathnames as a subtype of `AbstractString`

I think this could be a neat approach; the only caveat I see is supporting all of the string interface as the set of valid pathnames may not be closed under all string operations. Eg what happens for your type `*` an arbitrary string (may or may not be a pathname)?

But in any case, I think this is worth experimenting with.

---

<div class="post-metadata">

**Author:** ![rdeits](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rdeits/32/286_2.png) [@rdeits](https://discourse.julialang.org/u/rdeits)\
**Post date:** [March 31, 2020, 12:51pm UTC](https://discourse.julialang.org/t/better-handling-of-pathnames/36792/3 "2020-03-31T12:51:25Z")

</div>

> [@jessymilare](#):
>
> I think it’s not a nice practice to define methods belonging to another one’s package for another one’s classes (e.g. `csv_read(file::Path; kwargs...) = CSV.read(string(file); kwargs...)` ), since a third person could redefine them another way.

By the way, you are 100% right about this. The term used in Julia for defining someone else’s function on someone else’s type is “type piracy”.

> [@jessymilare](#):
>
> For that reason, I was thinking about creating a new package with pathnames as a subtype of `AbstractString` , specifying a unique string representation for each pathname (like TCL does, what a nice language!). That way, one deal with pathnames as structures and use them in methods that expects strings.

I think this would be nice. The fact that we have a function `joinpath()` rather than `join(::AbstractPath...)` feels like an indication that path-specific types could be useful.

---

<div class="post-metadata">

**Author:** ![Jordan\_Cluts](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jordan_cluts/32/13753_2.png) [@Jordan\_Cluts](https://discourse.julialang.org/u/Jordan_Cluts)\
**Post date:** [March 31, 2020, 1:16pm UTC](https://discourse.julialang.org/t/better-handling-of-pathnames/36792/4 "2020-03-31T13:16:48Z")

</div>

No discussion of Paths is complete without pointing out [FilePathsBase.jl](https://github.com/rofinn/FilePathsBase.jl). It doesn’t solve the particular issue you’re discussing but it is worth being aware of if you’re working and thinking about solutions in this space.

---

<div class="post-metadata">

**Author:** ![davidanthoff](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/davidanthoff/32/223493_2.png) [@davidanthoff](https://discourse.julialang.org/u/davidanthoff)\
**Post date:** [March 31, 2020, 4:56pm UTC](https://discourse.julialang.org/t/better-handling-of-pathnames/36792/5 "2020-03-31T16:56:38Z")

</div>

FilePaths.jl and FilePathsBase.jl used to inherit from `AbstractString`, and then that was dropped at some point [Drop string subtyping by rofinn · Pull Request #22 · rofinn/FilePathsBase.jl · GitHub](https://github.com/rofinn/FilePathsBase.jl/pull/22).

I think the most helpful thing at this point would be to polish FilePathsBase, to the point where it might be ready to make it into the stdlib at some point.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [March 31, 2020, 5:12pm UTC](https://discourse.julialang.org/t/better-handling-of-pathnames/36792/6 "2020-03-31T17:12:03Z")

</div>

I find the [reasoning for making this change](https://github.com/rofinn/FilePathsBase.jl/issues/15) a bit odd. They wrote _the only reason we’ve been subtyping it is to make interop with existing filesystem methods easier_. Compatibility with all existing code that uses file paths does not seem like a minor benefit to me! Without it, I find this approach to be of very limited utility — it’s not practical to require all existing filesystem code (in both Base and packages) to be updated, or to require every caller to perform a conversion.

Yes, implementing a full-featured `AbstractString` interface does require you to implement a fair number of methods, but the benefits of compatibility are huge.

---

<div class="post-metadata">

**Author:** ![jessymilare](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jessymilare/32/13750_2.png) [@jessymilare](https://discourse.julialang.org/u/jessymilare)\
**Post date:** [March 31, 2020, 5:41pm UTC](https://discourse.julialang.org/t/better-handling-of-pathnames/36792/7 "2020-03-31T17:41:37Z")

</div>

I can’t imagine another reason for subtyping anything other than interoperability with existing code/functionality.

---

<div class="post-metadata">

**Author:** ![davidanthoff](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/davidanthoff/32/223493_2.png) [@davidanthoff](https://discourse.julialang.org/u/davidanthoff)\
**Post date:** [March 31, 2020, 8:01pm UTC](https://discourse.julialang.org/t/better-handling-of-pathnames/36792/8 "2020-03-31T20:01:13Z")

</div>

I can see that I was in favor of dropping the inheritance from `AbstractString` back when that change was made, but I have to admit I no longer understand why… Maybe we should revisit that?

The only reasoning I can come up with now is that maybe one wants to encourage API design where a `AbstractString` is _not_ treated as a path? Say `parse(x::AbstractString)` would parse the content of `x` directly, and `parse(x::Path)` would load a file and parse. But of course that even works if `Path` inherits from `AbstractString`, so that is probably not a great argument.

---

<div class="post-metadata">

**Author:** ![affans](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/affans/32/11911_2.png) [@affans](https://discourse.julialang.org/u/affans)\
**Post date:** [March 31, 2020, 8:45pm UTC](https://discourse.julialang.org/t/better-handling-of-pathnames/36792/9 "2020-03-31T20:45:53Z")

</div>

Isn’t the easier way of doing this simply support `::Path` in `CSV` or other packages that deal with it? This shifts the burden to package maintainers to have a function `csv_read(::Path)` but isn’t this a feature of Julia? i.e. that whether using a string or `Path`, it should just “work”?

---

<div class="post-metadata">

**Author:** ![heliosdrm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/heliosdrm/32/3851_2.png) [@heliosdrm](https://discourse.julialang.org/u/heliosdrm)\
**Post date:** [March 31, 2020, 9:12pm UTC](https://discourse.julialang.org/t/better-handling-of-pathnames/36792/10 "2020-03-31T21:12:24Z")

</div>

I don’t think that CSV should add new methods to `read` etc. to support `::Path`. Rather the contrary, I think that the advice is keeping such functions as generic as possible, such that they will “magically” work even with new types that the authors never knew of, if those types (like the proposed `Path <: AbstractString`) are created with a sufficiently rich interface.

That’s the message that I got from this video, I hope I interpreted it in the right way:

[![](https://global.discourse-cdn.com/julialang/original/3X/c/5/c5a2f02c81c376da005f8610ce9cdd4ad47cfdf4.jpeg "JuliaCon 2019 | The Unreasonable Effectiveness of Multiple Dispatch | Stefan Karpinski") ](https://www.youtube.com/watch?v=kc9HwsxE1OY)

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [March 31, 2020, 10:11pm UTC](https://discourse.julialang.org/t/better-handling-of-pathnames/36792/11 "2020-03-31T22:11:22Z")

</div>

> [@affans](#):
>
> Isn’t the easier way of doing this simply support `::Path` in `CSV` or other packages that deal with it?

So, rather than implementing a couple dozen methods to implement an `AbstractString` interface in _one_ package, you think it is simpler to change _every_ package that works with files?

Out of the [3000+ Julia packages](https://discourse.julialang.org/t/over-3000-packages-congratulation-julia-community/36702), how many do you think accept a pathname? (Also, you’ll have to extend every `Base` function that accepts a pathname, of which there are quite a few.)

---

<div class="post-metadata">

**Author:** ![affans](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/affans/32/11911_2.png) [@affans](https://discourse.julialang.org/u/affans)\
**Post date:** [April 1, 2020, 6:08am UTC](https://discourse.julialang.org/t/better-handling-of-pathnames/36792/12 "2020-04-01T06:08:24Z")

</div>

Hmm, I didn’t think of it this way. So if `Path < AbstringString` that would would be easier since many methods across many packages already accept a string.

I always thought that as a package developer if I expose a type, it should be up to other package maintainers to write methods to dispatch on that type. I guess it depends on how “abstract” the type is.

---

<div class="post-metadata">

**Author:** ![Oscar\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscar_smith/32/25343_2.png) [@Oscar\_Smith](https://discourse.julialang.org/u/Oscar_Smith)\
**Post date:** [April 1, 2020, 6:16am UTC](https://discourse.julialang.org/t/better-handling-of-pathnames/36792/13 "2020-04-01T06:16:01Z")

</div>

The whole reason multiple dispatch works so well is that if you write generic code, and someone makes a type that matches the interface, you get code re-use that no one had to plan. Stuff like this is why people in the discourse put so much emphasis on non over-typing your functions or structs.

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [April 1, 2020, 7:35am UTC](https://discourse.julialang.org/t/better-handling-of-pathnames/36792/14 "2020-04-01T07:35:31Z")

</div>

> [@stevengj](#):
>
> So, rather than implementing a couple dozen methods to implement an `AbstractString` interface in _one_ package, you think it is simpler to change _every_ package that works with files?

Even if it is made to be `<:AbstractString`, maybe a path does not need to support _all_ of the relevant interface. Eg it could be perfectly reasonable for `*` to error on results that are not valid paths, or can’t be interpreted as such.

---

<div class="post-metadata">

**Author:** ![Daneel](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/daneel/32/175_2.png) [@Daneel](https://discourse.julialang.org/u/Daneel)\
**Post date:** [April 2, 2020, 6:29am UTC](https://discourse.julialang.org/t/better-handling-of-pathnames/36792/15 "2020-04-02T06:29:42Z")

</div>

Wouldn’t it be possible to have methods for `*` which simply call `joinpath` if one of the inputs is a path? That would certainly add to the readability of path generation.

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [April 2, 2020, 6:44am UTC](https://discourse.julialang.org/t/better-handling-of-pathnames/36792/16 "2020-04-02T06:44:10Z")

</div>

Note however that they are not equivalent:

```julia
julia> "a" * "B"
"aB"

julia> joinpath("a", "B")
"a/B"

```

so if paths behave like strings this could be confusing.

I would recommend keeping the current behavior for both `*` and `joinpath`, and suggest that the path API is used for all path manipulations.

---

<div class="post-metadata">

**Author:** ![Daneel](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/daneel/32/175_2.png) [@Daneel](https://discourse.julialang.org/u/Daneel)\
**Post date:** [April 2, 2020, 6:58am UTC](https://discourse.julialang.org/t/better-handling-of-pathnames/36792/17 "2020-04-02T06:58:46Z")

</div>

That’s the point but you are right, it’s not a design decision without consequence.

```julia
julia> programPath = path("C:\\Programs\\My_Program");
julia> execPath = programPath * "bin" * "prog.exe"
Path: C:\Programs\My_Program\bin\prog.exe

```

vs

```julia
julia> programPath = "C:\\Programs\\My_Program";
julia> execPath = joinpath(programPath,"bin","prog.exe")
"C:\\Programs\\My_Program\\bin\\prog.exe"

```

The former is more readable in my opinion and I would expect the overlap in usage wouldn’t be frequent or unclear.

```julia
julia> programPath = "C:\\Programs\\My_Program";
julia> println("Executable Path: " * string(programPath * "bin" * "prog.exe"))
Executable Path: C:\Programs\My_Program\bin\prog.exe

```

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [April 2, 2020, 7:02am UTC](https://discourse.julialang.org/t/better-handling-of-pathnames/36792/18 "2020-04-02T07:02:15Z")

</div>

> [@Daneel](#):
>
> ```julia
> julia> execPath = programPath * "bin" * "prog.exe"
> Path: C:\Programs\My_Program\bin\prog.exe
> 
> ```

But this would violate basic assumptions of the `AbstractString` interface. You can either

1. have a path type `<: AbstractString`, then `*` has to do what it does for strings,

2. have a path type that is not a string, supports `*` for `joinpath` (and, of course, requires rewriting a ton of package code that assumes paths are strings).

You can’t have it both ways. And I don’t think that `*` as an alias for `joinpath` is worth a breaking change.

---

<div class="post-metadata">

**Author:** ![tkf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkf/32/17635_2.png) [@tkf](https://discourse.julialang.org/u/tkf)\
**Post date:** [April 2, 2020, 8:08am UTC](https://discourse.julialang.org/t/better-handling-of-pathnames/36792/19 "2020-04-02T08:08:36Z")

</div>

Other things you can have if path is not a string are specialized `getindex` and `iterate`. It’s kind of cute if you can do `path[end]` to mean `basename(path)`. Things like `path[end-3:end]` for constructing relative path is useful sometimes.

---

<div class="post-metadata">

**Author:** ![jmkuhn](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jmkuhn/32/2090_2.png) [@jmkuhn](https://discourse.julialang.org/u/jmkuhn)\
**Post date:** [April 2, 2020, 2:42pm UTC](https://discourse.julialang.org/t/better-handling-of-pathnames/36792/20 "2020-04-02T14:42:54Z")

</div>

I like the current behavior of FilePaths.jl where `/` is `joinpath` and `*` is regular string concatenation.

```julia
julia> p"/dir" / p"subdir" / "file" * ".ext"
p"/dir/subdir/file.ext"

```

[Next page](https://discourse.julialang.org/t/better-handling-of-pathnames/36792.md?page=2)
