Re: language inconsistency(wart) & RFE

2015-10-22 Thread Greg Wooledge
On Wed, Oct 21, 2015 at 07:19:30PM -0700, Linda Walsh wrote:
> Not to mention your statement doesn't work:

My code worked.  I tested it.  If you take working code and mangle it
according to your incredibly bizarre notions of how bash code should
look, and it doesn't work, then the problem is on your end.

> function nint {
>  local i j k
>  for ((i=0; i<10; ++i)) ; do j+=i; k+=j ;done

THAT.  IS.  NOT.  HOW.  YOU.  DO.  ARITHMETIC.

You want to perform integer addition?  Then you use the syntax that
specifies integer addition.

j+=i   is the syntax that specifies string concatenation.

To perform arithmetic, you use $((..)) or ((..)) or let.

nint() {
  local i j k
  for ((i=0; i<10; i++)); do ((j+=i, k+=j)); done
  declare -p i j k
}


imadev:~$ nint
declare -- i="10"
declare -- j="45"
declare -- k="165"


I have stated this before, but apparently it didn't sink it, so I'll
try again in all caps: DO NOT USE declare -i EVER.

If you use declare -i then you BREAK the ability to read your script and
see what a command is doing.  You pervert the string concatention syntax
into performing integer addition, but only some of the time, and you can't
tell which times that is.  (OO people call this "overloading" and think
that it's a feature rather than an abomination.)  (I do not and never
will understand OO people.  They must have been brainwashed.)

So now that you know not to use declare -i, all of your other problems
just go away!

You use local when you want local variables, and you don't use any
declaration at all when you want global (or dynamically scoped)
variables.

That provides the ability you have been asking for: to decide on a
case-by-case basis which variables will be local and which will not.

The end.



Re: language inconsistency(wart) & RFE

2015-10-21 Thread Linda Walsh



Greg Wooledge wrote:

On Sat, Oct 17, 2015 at 08:55:31AM -0700, Linda Walsh wrote:

If I needed a way to declare something global, yes...
But what I am wanting is a way to allow changing the defaults
of the implicit variable creation (which could still be
explicitly declared with "-g" if one wanted their result to be
made global.


So you are basically saying you want all of your function variables
to be local

---
No...  only ones where 'shopt -s auto_local' was in effect.

but you are too lazy to write 'local i j k' and you want

bash to do it for you?


local i j k doesn't define j as an array or k as a hash
or g (w/-g ) as a global or any case modification vars.
Not to mention your statement doesn't work:

function nint {
 local i j k
 for ((i=0; i<10; ++i)) ; do j+=i; k+=j ;done
 echo "i=$i, j=$j, k=$k"
}


nint && echo "($i, $j, $k)"

i=10, j=ii, k=jj
(, , )

You declared them all the same, but at the end of the
function, j and k do not have integer values.

Trying to init them:
function nint {
 local i=0 j=0 k=0

 for ((i=0; i<10; ++i)) ; do
 j+=i
 k+=j
done
my -p i j k; echo "i=$i, j=$j, k=$k"
}

nint && echo "($i, $j, $k)"
declare -- i="10"
declare -- j="0ii"
declare -- k="0jj"
i=10, j=0ii, k=0jj
(, , )
---
Now we see that i & j & k all have the same
attributes ... yet only 'i' has an integer value.

If you could declare all the vars in 1 statement w/different types:

declare -i INT -a ARRAY -A HASH
---
That would be a huge improvement.  But instead,if you
use a 'for' statement, without pre-declaring all the vars
used, you end up leaking variables:

function nint {

for ((i=0; i<10; ++i)) ; do
j+=i   
k+=j

done
my -p i j k; echo "i=$i, j=$j, k=$k"
}

nint && echo "($i, $j, $k)"

declare -- i="10"
declare -- j="ii"
declare -- k="jj"
i=10, j=ii, k=jj
(10, ii, jj)
---
"leaking variables" into the external environment
is almost always considered bad-practice.

The default that bash encourages with its default behavior
is for all implicitly used vars in a function to be
leaked to the global level.  From a program maintenance
and development standpoint, having such a horrible default
with no way to override it just seems really icky.













Also I think you are completely misrepresenting the dynamic variable
scope system that bash uses.  Variables are not just global or local.
There's an entire stack of them.  When you reference a variable (let's
say i) inside a function, bash searches up through the call stack
looking for a variable named i until it finds one.

Since functions cannot return values to their callers, the entire system
of "put values into an upper-scope variable so the caller can see them"
would break if your proposal of automatic localization were to be
adopted.


# Pick unbiased random number from 0 to N-1 ($1 = N)
# Returns value in variable r.
rand() {
  local max=$((32768 / $1 * $1))
  while (( (r=$RANDOM) >= max )); do :; done
  r=$(( r % $1 ))
}

foo() {
  local r
  rand 6
  echo "I rolled $((r+1))"
}

foo
# r is not visible here


Under your proposal, the variable r which is defined locally in foo, and
is up-scope-visible to rand (so that rand can put a return value into
it), would also be defined locally within r, so there would be no way to
return a value from rand to foo.

(If you want to attack "language warts", start with the inability to
return values from functions to their callers!)





Re: language inconsistency(wart) & RFE

2015-10-21 Thread Linda Walsh

Here is an example of what I consider to be
strange and/or inconsistent behavior (  tests 
2 & 3 fail).


test 3's failure is a real puzzler:

-
#!/bin/bash

shopt -s expand_aliases ; alias my=declare
alias array='my -a' int='my -i'

dmp () { printf "%q\n" "${*:?}" ; }

ip=10.20.30.40
array  parts=()
(IFS=.; parts=( $ip ) )
array answers=( 
 '10 20 30 40'  
 '1 .2 .3 .4'
 '1 222 .3 .4' 
 '192.168.0.1/24::{ [ipaddr]="192.168.0.1/24" [address]="192.168.0.1" [prefixlen]="24" ; }'

)
#


array addr_fields=(ipaddr address prefixlen)
 
assignparts() { parts=( $ip ) ; }


tst0 () { my IFS=. ; assignparts ; echo -E "${parts[@]}"; }
tst1 () { my IFS=0 ; assignparts ; echo -E "${parts[@]}"; }
tst2 () { parts[1]=222; echo -E "${parts[@]}" ; }

tst3 () {
 my ipaddr="$1"; shift;
 int status=0
 my pat='^([^/]+)/([0-9]+)\s*$'
 my address prefixlen
 if [[ ! $ipaddr =~ $pat ]]; then
   echo >&2 "Error in ip/netsize format: \"$ipaddr\""
   status=1
 else
   address=${BASH_REMATCH[1]}
   prefixlen=${BASH_REMATCH[2]}
   my out=""
   for flds in "${addr_fields[@]}"; do
 out+="[$flds]=\"${!flds}\" "
   done
   printf "{ %s; }" "$out"
 fi
 return $status
}



int passno=0 tests=0
my fmt="Case %d got/Expected:\n \"%q\"\n \"%q\"\n"


testor () {
 int tstno
 my out=""
 for tstno in {0..3}; do
   tests+=1
   exp="${answers[tstno]}"
   if [[ $exp =~ :: ]]; then
 my args="${exp%::*}"
 exp="${exp#*::}"
 out="$(tst$tstno $args)"
   else
 out="$(tst$tstno)"
   fi
   if [[ $out != $exp ]]; then
 printf >&2 "$fmt" "$tstno" "$out" "$exp"
 continue
   fi
   passno+=1
 done
}

testor
echo "Passed $passno/$tests tests."


output:
Case 2 got/Expected:
"222"
"1\ 222\ .3\ .4"
Case 3 got/Expected:
"\{\ \[ipaddr\]=\"192.168.0.1/24\"\ \[address\]=\"192.168.0.1\"\ 
\[prefixlen\]=\"24\"\ \;\ \}"
"\{\ \[ipaddr\]=\"192.168.0.1/24\"\ \[address\]=\"192.168.0.1\"\ 
\[prefixlen\]=\"24\"\ \;\ \}"
Passed 2/4 tests.

The outputs for case 3 look identical -- I was using %s to print
them out, but switched to "%q", to ensure no hidden chars...

case 2 --
???  Why didn't it only change the 1 member?






Re: language inconsistency(wart) & RFE

2015-10-21 Thread Linda Walsh



Greg Wooledge wrote:
--- please note locations of 'auto_local'

# at beginning
shopt -s auto_local
# Pick unbiased random number from 0 to N-1 ($1 = N)
# Returns value in variable r.
rand() {
  shopt -u auto_local
  local max=$((32768 / $1 * $1))
  while (( (r=$RANDOM) >= max )); do :; done
  r=$(( r % $1 ))
  shopt -s auto_local
}

foo() {
  local r
  rand 6
  echo "I rolled $((r+1))"
}

foo
# r is not visible here


Under your proposal, the variable r which 

is defined locally in foo, and

is up-scope-visible to rand (so that rand can put a return value into
it), would also be defined locally within r, 

so there would be no way to

return a value from rand to foo.


Under my proposal, it is a shopt option that can be turned
on or off.  When it is off, things behave as they do no.  When it is
on, vars w/o a specific declarator, or declarator+"-g switch" would
be default to be local. 




(If you want to attack "language warts", start with the inability to
return values from functions to their callers!)

---
They can -- unfortunately, it is by printing to, usually,
to stdout, and reading it in the parent (I agree this is a rather
roundabout and inefficient way to send return vals... 


I've thought about that, but running into inconsistencies in the
dynamic variable propagation.  It's hard to define an
"alternate syntax" when the primary syntax doesn't consistently
yield the same answer.  So need to figure that out first.

Hopefully the toggling of 'auto_local' isn't hard to understand:
it's like adding a -i or -l flag to var declaration -- doesn't
affect current values in vars, but only new assignments.






Re: language inconsistency(wart) & RFE

2015-10-19 Thread Chet Ramey
On 10/16/15 9:18 PM, Linda Walsh wrote:
> Ok, thinking this from a different way.
> 
> shopt -s implicit_vars_local
> or
> shopt -s localize_func_implicit_vars whatever...
> 
> Right now, in a function, you *can* use local in a function
> to make a local var.  Thing is, both 'declare' and 'typeset' also
> make a *local* var in a function, unless the "-g" switch is used.
> 
> I.e. All standard, overt ways (local declare typeset) of creating
> a var in a function all result in it being local, BUT, (and I think this is
> an ugly wart), any *implicit vars* without local, or the misleading declare
> or typeset, become global.

All variables are created at the global scope unless explicitly designated
as local with local/declare/typeset.  Simple and straightforward.  (Dynamic
scoping does change that a little, as has been covered ad nauseum.)

-- 
``The lyf so short, the craft so long to lerne.'' - Chaucer
 ``Ars longa, vita brevis'' - Hippocrates
Chet Ramey, ITS, CWRUc...@case.eduhttp://cnswww.cns.cwru.edu/~chet/



Re: language inconsistency(wart) & RFE

2015-10-19 Thread Greg Wooledge
On Sat, Oct 17, 2015 at 08:55:31AM -0700, Linda Walsh wrote:
>   If I needed a way to declare something global, yes...
> But what I am wanting is a way to allow changing the defaults
> of the implicit variable creation (which could still be
> explicitly declared with "-g" if one wanted their result to be
> made global.

So you are basically saying you want all of your function variables
to be local, but you are too lazy to write 'local i j k' and you want
bash to do it for you?

Also I think you are completely misrepresenting the dynamic variable
scope system that bash uses.  Variables are not just global or local.
There's an entire stack of them.  When you reference a variable (let's
say i) inside a function, bash searches up through the call stack
looking for a variable named i until it finds one.

Since functions cannot return values to their callers, the entire system
of "put values into an upper-scope variable so the caller can see them"
would break if your proposal of automatic localization were to be
adopted.


# Pick unbiased random number from 0 to N-1 ($1 = N)
# Returns value in variable r.
rand() {
  local max=$((32768 / $1 * $1))
  while (( (r=$RANDOM) >= max )); do :; done
  r=$(( r % $1 ))
}

foo() {
  local r
  rand 6
  echo "I rolled $((r+1))"
}

foo
# r is not visible here


Under your proposal, the variable r which is defined locally in foo, and
is up-scope-visible to rand (so that rand can put a return value into
it), would also be defined locally within r, so there would be no way to
return a value from rand to foo.

(If you want to attack "language warts", start with the inability to
return values from functions to their callers!)



Re: language inconsistency(wart) & RFE

2015-10-17 Thread Linda Walsh



isabella parakiss wrote:


Maybe you can just use this?alias declare='declare -g'


If I needed a way to declare something global, yes...
But what I am wanting is a way to allow changing the defaults
of the implicit variable creation (which could still be
explicitly declared with "-g" if one wanted their result to be
made global.

For example, I use some aliases in documenting the
intended usage of vars...  especially with respect to 
integers, arrays and hashes.  Declaring a hash by calling

it a 'hash' is somewhat generic, but if I call it a 'map', it
is more clear from a documentation standpoint that I am
trying to use something as a way to map names to a value.  


So I will tend to pre-declare many of my vars, but
some are just an annoyance to predeclare.

Like readarray -t config <~/.toolrc -- read a config
file into an array, and then parse it for relevant options &&
then throw the "config array" out when done parsing it.  
Having to "declare" a temporary, _first_, before usage is

a bit backwards...

Obvious example is using throw-away names as
index or iterator vars.  I believe it to be rare, that
people use "for nam in "${names[@]}"; or
"for ((i=0;i<10;++i));" as a means to declare nam or 'i' as
"global variables.  They are  too meaningless for use as
a global, and much more likely to collide with usage of such
'tmp' names in other functions that may be called in a nested
fashion.  


Allowing the option to default to private state,
would take care of accident collisions where one loop uses 'i',
but calls some util function elsewhere that also uses 'i'
and ending up with strange side-effects.  It removes default
behavior to suppress accidental "spooky actions at a distance",
where one or both functions didn't pre-declare 'i' in a func.

It seems to me, that pre-declaring a variable draws
attention to it -- which is more wanted if it is a global,
but drawing attention to 'i' in a forloop by pre-declaring
it makes the code more confusing, as you are drawing attention
to a temporary who's value you usually won't care about after
the function has exit'ed.  


Allowing an option to change this default, allows for
more portable, more reliable and less confusing code to be
written, which would seem like a desirable goal.






Re: language inconsistency(wart) & RFE

2015-10-16 Thread isabella parakiss
On 10/17/15, Linda Walsh  wrote:
> Ok, thinking this from a different way.
>
> shopt -s implicit_vars_local
> or
> shopt -s localize_func_implicit_vars whatever...
>
> Right now, in a function, you *can* use local in a function
> to make a local var.  Thing is, both 'declare' and 'typeset' also
> make a *local* var in a function, unless the "-g" switch is used.
>
> I.e. All standard, overt ways (local declare typeset) of creating
> a var in a function all result in it being local, BUT,
> (and I think this is an ugly wart),
> any *implicit vars* without local, or the
> misleading declare or typeset, become global.
>
> examples:  In these two for statements, used in functions, 'i'
> becomes global:
>
>   for((i=0; i<10; ++i)); do : done
>   for i in {1..10}; do : done
>
> And same with 'myarray':
>   readarray myarray=$(echo "one";echo "two")
>
> and reads and assignments, and 'lets'
>   read ln < <(echo "one"; echo "two")
>   ln2="one two"
>   read ln3 <<< "one two"
>
> but if this isn't a potential for confusion:
>
>> function aa {
> read ln < <(echo "one"; echo "two")
> ln2="12"
> read ln3 <<< "one two"
> ar1=(one two)
> typeset -i ln2=34
> typeset -a ar1=(three four)
> }
>> whence aa
> aa is a function
> aa ()
> {
> read ln < <(echo "one"; echo "two");
> ln2="12";
> read ln3 <<< "one two";
> ar1=(one two);
> typeset -i ln2=34;
> typeset -a ar1=(three four)
> }
>> aa
>> declare -p ln ln2 ln3 ar1
> declare -- ln="one"
> declare -- ln2="12"
> declare -- ln3="one two"
> declare -a ar1='([0]="one" [1]="two")'
>
> !!!  -- sure looks like I was trying to set the "type" of ln2
> and ar1... boy could that be confusing...
>
> 
> So, how about a "shopt"
> to declare that **what's implicity declared in funcs, stays in funcs**
> maybe shopt -s vegasvars ?.
>
> but seriously -- it's so odd that anything you declare explicitly
> becomes local, while implicit vars default to global --
> I know standards and compat must keep it that way... BUT
> it would have made more sense to have
> implicit vars in a function always be 'local'
> and maybe have explicit declarators be global
> (which has effectively been done with -g)...but
> originally, I also thought it strange that 'declare/typeset'
> were equivalent to 'local' inside a function.
>
> This way, you wouldn't have to change any syntax
> parsing functions and there certain isn't ANYTHING
> that would look like perl, even though perl was
> originally designed to be like shell with many shell standard
> functions built-in.
>
> ???
>
>
>

Maybe you can just use this?alias declare='declare -g'


---
xoxo iza



language inconsistency(wart) & RFE

2015-10-16 Thread Linda Walsh

Ok, thinking this from a different way.

shopt -s implicit_vars_local
or
shopt -s localize_func_implicit_vars whatever...

Right now, in a function, you *can* use local in a function
to make a local var.  Thing is, both 'declare' and 'typeset' also
make a *local* var in a function, unless the "-g" switch is used.

I.e. All standard, overt ways (local declare typeset) of creating
a var in a function all result in it being local, BUT, 
(and I think this is an ugly wart), 
any *implicit vars* without local, or the 
misleading declare or typeset, become global.


examples:  In these two for statements, used in functions, 'i'
becomes global:

 for((i=0; i<10; ++i)); do : done
 for i in {1..10}; do : done

And same with 'myarray':
 readarray myarray=$(echo "one";echo "two")

and reads and assignments, and 'lets'
 read ln < <(echo "one"; echo "two")
 ln2="one two"
 read ln3 <<< "one two"

but if this isn't a potential for confusion:

function aa {   

read ln < <(echo "one"; echo "two")
ln2="12"
read ln3 <<< "one two"
ar1=(one two) 
typeset -i ln2=34

typeset -a ar1=(three four)
}

whence aa

aa is a function
aa () 
{ 
   read ln < <(echo "one"; echo "two");

   ln2="12";
   read ln3 <<< "one two";
   ar1=(one two);
   typeset -i ln2=34;
   typeset -a ar1=(three four)
}

aa
declare -p ln ln2 ln3 ar1

declare -- ln="one"
declare -- ln2="12"
declare -- ln3="one two"
declare -a ar1='([0]="one" [1]="two")'

!!!  -- sure looks like I was trying to set the "type" of ln2 
and ar1... boy could that be confusing...  



So, how about a "shopt" 
to declare that **what's implicity declared in funcs, stays in funcs**

maybe shopt -s vegasvars ?.

but seriously -- it's so odd that anything you declare explicitly 
becomes local, while implicit vars default to global --

I know standards and compat must keep it that way... BUT
it would have made more sense to have
implicit vars in a function always be 'local'
and maybe have explicit declarators be global
(which has effectively been done with -g)...but
originally, I also thought it strange that 'declare/typeset'
were equivalent to 'local' inside a function.

This way, you wouldn't have to change any syntax
parsing functions and there certain isn't ANYTHING
that would look like perl, even though perl was
originally designed to be like shell with many shell standard
functions built-in.

???