while IFS= read; do :; done < file; echo $REPLY
The above reads each line of the file, one by one. After it reads the last line of the file, it goes through the loop and then it tries to read once more. It is this last attempt to read that generates end-of-file condition and the empty $REPLY.
read < file; echo $REPLY
By default, read reads one line at a time. You only ask for one line so that is what is in $REPLY. (If one specifies the -d option to read, then $REPLY could have the whole file in it depending on the chosen delimiter.
cat file | read; echo $REPLY
This is a pipeline. This means that read is in a subshell. Therefore, shell variables that it creates do not survive.
If your goal is to get the last line of the file into a variable, there are options. For example:
reply="$(tail -n1 file)" ; echo $reply
Or,
reply="$(sed -n '$p' file )" ; echo $reply
(In sed-speak, $ means the last line of the file and p means print. So, $p means print the last line of the file.)