The range operator certainly is valid in scalar context, and indeed is very useful indeed. However, in that context it has nothing to do with ranges

It acts as a flip-flop (see perlop).
In scalar context, it begins by checking the left operand for truth. Since the left operand is a constant expression in this case (2), it compares it to the current value of the $. variable (see perlvar to see what that's for if you don't already). If $. is not equal to 2, it returns a false value (the empty string), which when used as an array index is the same as 0. In fact, since you haven't yet read from a file, $. is undefined, which is why you're getting the uninitialised warning.
If $. happened to be equal to 2 (i.e. you'd read exactly two lines from the most recently accessed filehandle), the `..' operator would return true (1) and so you'd get the first element in the array.
Look at these three examples (pass some file to the script on the command line so that there's some lines for it to read).
First one - uninitialised warning - array element not numeric warning - prints "zero"
Code:
#!/usr/bin/perl -w
use strict;
my $bal = 'fda';
my @array = qw/zero one two three four/;
print $array[2..$bal],"\n";
We've now read one line from <>, so $. is 1. No uninitialised warning anymore, since it has a value. Still prints "zero", since it's not equal to 2
Code:
#!/usr/bin/perl -w
use strict;
my $stuff = <>;
my $bal = 'fda';
my @array = qw/zero one two three four/;
print $array[2..$bal],"\n";
Now we've read two lines so $. equals "2". The ".." operator now returns 1, so this prints "one".
Code:
#!/usr/bin/perl -w
use strict;
my $stuff = <>;
$stuff = <>;
my $bal = 'fda';
my @array = qw/zero one two three four/;
print $array[2..$bal],"\n";
Hopefully someone has an idea of what I'm talking about!
BTW good spot on the scalar context Kevin, I hadn't noticed that at all.