File Paths in Erlang

In Erlang, we don’t have a direct equivalent to the filepath module, but we can use the filename module to handle file paths in a portable way. Here’s how we can achieve similar functionality:

-module(file_paths).
-export([main/0]).

main() ->
    % Join should be used to construct paths in a portable way.
    % It takes any number of arguments and constructs a hierarchical path from them.
    P = filename:join(["dir1", "dir2", "filename"]),
    io:format("p: ~p~n", [P]),

    % You should always use join instead of concatenating separators manually.
    % In addition to providing portability, join will also normalize paths.
    io:format("~p~n", [filename:join("dir1//", "filename")]),
    io:format("~p~n", [filename:join("dir1/../dir1", "filename")]),

    % dirname and basename can be used to split a path to the
    % directory and the file.
    io:format("Dir(p): ~p~n", [filename:dirname(P)]),
    io:format("Base(p): ~p~n", [filename:basename(P)]),

    % We can check whether a path is absolute.
    io:format("~p~n", [filename:pathtype("dir/file")]),
    io:format("~p~n", [filename:pathtype("/dir/file")]),

    Filename = "config.json",

    % Some file names have extensions following a dot.
    % We can split the extension out of such names with extension.
    Ext = filename:extension(Filename),
    io:format("~p~n", [Ext]),

    % To find the file's name with the extension removed,
    % use filename:rootname.
    io:format("~p~n", [filename:rootname(Filename)]),

    % relative_to finds a relative path between a base and a target.
    % It returns an error if the target cannot be made relative to base.
    case filename:relative_to("a/b/t/file", "a/b") of
        {ok, Rel} -> io:format("~p~n", [Rel]);
        {error, _} -> io:format("Error in relative_to~n")
    end,

    case filename:relative_to("a/c/t/file", "a/b") of
        {ok, Rel2} -> io:format("~p~n", [Rel2]);
        {error, _} -> io:format("Error in relative_to~n")
    end.

To run this program, save it as file_paths.erl and use the Erlang shell:

$ erl
1> c(file_paths).
{ok,file_paths}
2> file_paths:main().
p: "dir1/dir2/filename"
"dir1/filename"
"dir1/filename"
Dir(p): "dir1/dir2"
Base(p): "filename"
relative
absolute
".json"
"config"
"t/file"
Error in relative_to

Note that Erlang’s filename module handles paths slightly differently from Go’s filepath:

  1. filename:pathtype/1 returns relative, absolute, or volumerelative instead of a boolean.
  2. filename:relative_to/2 is used instead of Rel, and it returns {ok, RelativePath} or {error, Reason}.
  3. Erlang doesn’t have a direct equivalent to strings.TrimSuffix, but filename:rootname/1 achieves the same result for file extensions.

These differences reflect the unique characteristics and conventions of Erlang compared to Go, while still providing similar file path manipulation capabilities.