The problem is not that Mathematica can not find the file, which it would indicate with another message. The problem is that Needs insists in your package to create the context it loads and put it onto $Packages.
To achieve that you need BeginPackage["foo`"] and the corresponding EndPackage in that file. Using that standard idiom will also make it possible to use the function name without the full context. That will of course also become a more minimal solution for the OPs problem.
From your comment I understand that you want to load the package but not have the package context in $ContextPath. The answer of Simoon Rochester shows you several ways to achieve that. Here are some more:
use Needs as usual, but remove the package name from $ContextPath afterwards:
Needs["foo`"]; $ContextPath = DeleteCases[$ContextPath,"foo`"]
the following will do the same and is somewhat more elegant and in some senses more reliable:
Block[{$ContextPath=$ContextPath},Needs["foo`"]]
but using Block in that way might have additional subtleties in specific situations.
Another thing you can do is to use two contexts, one for loading, the other one as the namespace of your symbols. If you put the following into file loadfoo.m:
BeginPackage["loadfoo`"]; EndPackage[]; Begin["foo`"]; hello = Function[{}, Print["Hello world!"]]; End[];
then
Needs["loadfoo`"]
will load with no error or warning messages and put "loadfoo`" onto $ContextPath but with no symbols in it. That file will also define foo`hello which you can use as you seem to intend.
EDIT jkuczm has silently changed a note given above which originally read that Needs expect the context to be added to $ContextPath to the more correct statement that it wants the context to be added to $Packages. Actually that is all that Needs really checks, a fact that I was not aware of as the pair of BeginPackage and EndPackage do more than that and the message that Needs just states that it wants the context to be created. Acutally you don't need to create any symbols in that context nor put it onto $ContextPath as I always assumed. So a file "test.m" anywhere in $Path will be loaded with no error messages by Needs if it contains just this:
Unprotect[$Packages]; AppendTo[$Packages,"test`"]; Protect[$Packages];
Using that idea, putting the following into a file "foo.m" should also solve the OPs problem without any special care by the user who loads and without adding an empty context to $ContextPath:
Unprotect[$Packages]; AppendTo[$Packages,"foo`"]; Protect[$Packages]; Begin["foo`"]; hello = Function[{}, Print["Hello world!"]]; End[];
which has the advantage that it can be loaded with just Needs["foo`"] and avoids any unnecessary side effects.
NeedscallsGetwhich "by default successively searches for files in the directories specified by the elements of $Path." $\endgroup$$Pathcan't possibly be the answer to my question, since there is no relationship between names of files and the names of contexts defined therein. IOW, a file namedfoo.mneed not define a contextfoo`, and, conversely, a file named something other thanfoo.m, can define a context namedfoo`. Since there's no correspondence whatsoever between context names and file names, I see no way in which the content of$Pathcould be used to find the where a context is defined. $\endgroup$Getjust loads a file and doesn't care what is in it.Needsshould be used to load a package that follows the standard structure. It usesGetinternally, but it also does extra checks and avoids double loading. $\endgroup$