In short: dynamic binding.
It is maybe a little unfortunate that
default-directory was used as the argument to
Note the docstring:
(expand-file-name NAME &optional DEFAULT-DIRECTORY)
Convert filename NAME to absolute, and canonicalize it.
Second arg DEFAULT-DIRECTORY is directory to start with if NAME is relative
(does not start with slash or tilde); both the directory name and
a directory’s file name are accepted. If DEFAULT-DIRECTORY is nil or
missing, the current buffer’s value of ‘default-directory’ is used.
By dynamically binding a
nil value for
default-directory around the call to
expand-file-name, that becomes both the argument passed by your function and the fall-back value which is used "If DEFAULT-DIRECTORY is nil or missing". That is the difference between the two variants, as normally the fall-back value would be a string.
You would get the same result with:
(let ((default-directory nil))
(expand-file-name "http://example.com" nil))
If you change the name of that argument in your function, this will stop happening. e.g.:
(defun wh/expand-file-name (name &optional defaultdir)
(expand-file-name name defaultdir))
expand-file-name itself is written in C and is not subject to this same behaviour, which is why it gets away with using that name as an argument, but you do not.