Are the values of foo and foo="" equivalent? If not, how is it possible to differ between these values
No, the former is pretty much like an unset variable. You could use something like declare -x foo to set flags to a variable without setting a value, e.g. here, to mark foo as exported in case it ever gets a value. If you can find a use for that. (It won't be actually exported to commands without a value.)
Use [[ -v name ]] (Bash/ksh/etc.) or [ "${var+set}" = set ] (standard) to tell the difference between an unset and a set-but-empty variable:
$ unset foo
$ declare foo
$ declare -p foo
declare -- foo
$ [[ -v foo ]] && echo is set || echo not set
not set
$ echo ${foo+set}
$ unset foo
$ foo=
$ declare -p foo
declare -- foo=""
$ [[ -v foo ]] && echo is set || echo not set
is set
$ echo ${foo+set}
set
Also, using declare var in a function makes the variable local to the function, a straight assignment doesn't, it assigns to the global variable instead:
$ foo=123
$ f() { declare foo; declare -p foo; }
$ f; declare -p foo
declare -- foo
declare -- foo="123"
$ g() { foo=; declare -p foo; }
$ g; declare -p foo
declare -- foo=""
declare -- foo=""
Going outside Bash, it seems that Ksh is similar to Bash here, and
that in Zsh, there's no difference between foo= and typeset foo, the latter also sets the variable to the empty string:
% unset foo; typeset foo; typeset -p foo; [[ -v foo ]] && echo is set || echo not set
typeset foo=''
is set
% unset foo; foo=; typeset -p foo; [[ -v foo ]] && echo is set || echo not set
typeset foo=''
is set
See also: