Rename File.rename/2 to File.mv/2

suggestion
#1

Seeing José’s new issue Add File.rename!, made me look into the module, and realize that we don’t have a “move” function, and File.rename/2 is the only one we have.
This has been inherited from Erlang, and actually, it is delegated to :file.rename/2.

From the File module:

This module also provides higher level functions that work with filenames and have their naming based on UNIX variants.

Historically (since the very first version of AT&T UNIX) mv had been the solution for renaming and moving files and directories.

The rename shell command works with Perl regular expressions. First argument is a regular expression, in the shape of ‘s/foo/bar’ or ‘y/A-Z/a-z/’, and the second argument is a list of files. So it is completely different than File.rename/2.

On the other hand, File.rename/2 mimics the way the mv shell command works, where first argument is the SOURCE and second argument is the DESTINATION.

Their uses are different, rename command is used to rename from one to a large quantities of files in one go. While File.rename/2 can only rename one file at a time.

So before adding a new File.rename!/2 function, and for the sake of consistency, I propose to consider having File.mv/2 and File.mv!/2.

References:

2 Likes
#2

mv and File.rename don’t work the same way however. If we had File.mv people would expect it to work like mv does, IE across file systems. This isn’t supported however by File.rename and wouldn’t be supported by File.mv, which would confuse people.

3 Likes
#3

I think that is something that can be explained in the documentation.
Also, you need to specify the name of the destination, if source is a file, and destination is a directory, you will get an :eisdir error (that’s already mentioned in the docs).

I’m not saying it behaves exactly the same way, but it works along the lines with its own limitations.

#4

1: It’s a standard library function, you need a VERY good reason to rename it.

2: In general if your docs need to explain why your function doesn’t behave the way your users think it does then it probably isn’t a well named function.

3: The whole point of renaming File.rename to File.mv would be to highlight how similar they are. However, they operate very differently, so this is just misleading and error prone. I’m not sure what the relevancy of perl is here. Elixir’s primary API inspirations are Erlang and Ruby, which both use rename, presumably to avoid this confusion.

4: Sake of consistency with what? Elixir’s File API simply isn’t the same thing as unix commands. Elixir’s File API should be internally consistent, and consistent with people’s understanding of what words mean with respect to file systems, but I don’t see any reason to argue that it should be word for word consistent with unix commands, particularly when they don’t do the same thing or act the same way.

4 Likes
#5

You are confusing the unix mv command with the rename() system call. There is no mv() system call. Using the mv command across filesystems actually uses cp & rm.

4 Likes
#6

But… it’s rename in C++, I’m sure C++ committee have some very wise people?

1 Like
#7

Lets, see:

Different names:

So I would say that your proposal is rather odd rather than standard. mv is only command name, not action, as others already said.

#8

Another thing that is important in using rename as a direct equivalent to the POSIX syscall is that the call is atomic. Because it only works on the local filesystem, the file is renamed conflict-free. This easily lets you do tricky operations where you fill in a temporary file and then write over the permanent one without risk of corrupting its content.

Doing a mv operation (delete + copy) does not have the same guarantees, and you might get funny behaviour with concurrency, order of operations being applied, and so on.

If you have a mv call in your library, you should at least keep the rename call as well, if only because, well, atomic renaming operations can turn out to be very important in some code’s critical sections.

EDIT: do note that rename is not necessarily “atomic” in the traditional sense – see https://stackoverflow.com/questions/7054844/is-rename-atomic for details

7 Likes
#9

Thank you everyone for your input.
The confusion stemmed from the fact that the File module mentions that the functions in this modules are named after the Unix commands.
Since v1 of AT&T Unix mv was the command, and there was no rename, later, POSIX adds rename as a system call, as pointed out by @joefractal, but it happens that GNU comes with a rename command which is used to renamed several files using regular expressions as arguments, which was written by Larry Wall (author of Perl, and therefore the mention to Perl Regex), that works totally different.
The fact that so many languages have used the rename name is because of the POSIX standard, and Erlang does so, and the error messages are defined by this standard and it adheres to it. And as far as I know it is the only one in the File module that behaves like the system call and not the Unix-like command.

As @ferd mentions rename/2 is atomic.
All this IMO should be mentioned in the rename documentation.
To make things even muddier the rename/2 docs make a mention to the mv command:

Note: The command mv in Unix systems behaves differently depending on
whether source is a file and the destination is an existing directory.
We have chosen to explicitly disallow this behaviour.

I will submit a PR, making a clarification that:

  • rename is based on the POSIX system call;
  • that the error messages are based on the POSIX standard and link to the it;
  • that it behaves different than rename command available in GNU and other Unix-like operating systems (if anybody else can contribute to this, I would appreciate it as I only use GNU/Linux);
  • mention about its atomicity;
  • Rephrase the “Note” where it talks about the mv command;

I will share the link to the PR once create, so if anyone wants to contribute is more than welcome.

2 Likes
#10

This is exactly why we decided to go with rename.

Not all functions in the module though. The higher level ones though, do. For example, we also have File.copy.

I don’t think it is important to refer to the system call as it is overly specific. That said, the docs could be better. I would mention about bullets 2 and 4 (error codes and atomicity) but forget the others and completely remove the note about mv.

1 Like
#11

But there are no copy command in Unix-like systems. The confusion happens because rename is a Unix-like command. As I mentioned later:

And as far as I know it is the only one in the File module that behaves like the system call and not the Unix-like command.