Perl has what may be an evil solution block locals.
Certainly maximal surprise but Does What I Mean…
What does the bit of Perl
$a{foo}++
do?
Well, you clearly are referring to a hash, so it makes one.
Since foo doesn’t have a sigil, it takes it as a string…
$a{‘foo’}++
Since the element doesn’t exists yet, and we’re adding one to it, it
"autovivifies" or “magically grants life” to it. Namely 0. Perl chose 0
since that is “What I Mean” when I add one to and uninitialised something.
Now to Ruby…
def each_anime(io)
io.each do |line|
next unless line =~ /evangelion|omg|spirited/
l = line.length # l is just local to block
map[$1] = line # Autovivifies map as Hash
count += 1 # Auto vivifies as 0 on first pass, an gives it scope
# beyond block.
weight *= 0.2 # Autovivifies as 1
yield line
end
l is out of scope here
[count,weight,map] # Visible at function scope as autovivified.
end
John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email : john.carter@tait.co.nz
New Zealand
A Million Monkeys can inflict worse things than just Shakespeare on
your system.
Just my personal feeling… but I’d hope to avoid stuff like this in
ruby.
It is fun, powerful and absolutely intrinsic and hard to understand.
[1]
I’m not saying this is really bad, just I’d prefer this does’nt
appear.
I strongly believe ‘explicit is better than implicit’.
plus, a single line:
map, count,weight={},0,1
would make this work. Not so much a big cost for me
[1]
If you’re going to say ‘no, let me explain, this is really clean and
intuitive’ please consider that i really think this is hard to grasp
Now I reread my post I see I didn’t make myself explicit. Sorry.
There has been debate on how to resolve the ugly problem of local’s in
Ruby in V2.0.
The suggestion I’m making is add autovivification, with the rule that
if a block local needs autovivifying you probably mean it to have
larger than block scope. (Since there is an implicit “local_var =
SomeClass.new” before the block)
This makes for very terse code.
def histogram( enum)
<— Implicit hash = Hash.new(0) here
enum.each{|e| hash[e]+=1} # Hash autovivifies.
Since hash autovivified we expect it to be available
outside the block scope.
hash.keys.sort{|a,b| hash[a]<=>hash[b]}.each do |k|
puts “#{k}\t#{hash[k]}”
end
end
John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email : john.carter@tait.co.nz
New Zealand
A Million Monkeys can inflict worse things than just Shakespeare on
your system.
···
On Tue, 2 Dec 2003, David A. Black wrote:
Hi –
On Tue, 2 Dec 2003, John Carter wrote:
Perl has what may be an evil solution block locals.
Certainly maximal surprise but Does What I Mean…
[…]
I’m sorry but I don’t think I understand your post. Are you asking
about how 2.0 is going to handle scope? I couldn’t quite follow.
Since hash autovivified we expect it to be available
outside the block scope.
hash.keys.sort{|a,b| hash[a]<=>hash[b]}.each do |k|
puts “#{k}\t#{hash[k]}”
end
end
How do you know that hash needs to be a hash? Depending on what enum is,
it could be an array or something else even.
As I understand it, variables first assigned to in a block in 2.0 are
equivalent to
variables in 1.8 with an implicit var = nil before the block, so they’re
visible
outside the block. Autovivifying to something other than nil seems more
dangerous
than it would be worth to me.
Autovivification in Perl is simplified by the fact that in the case
of “$a{$b}” Perl knows from the ‘{’ that a hash is involved.
In Ruby, it is not so easy, but clearly “a[b]” doesn’t make sense if
a.kind_of? Array and !b.kind_of?( FixNum), so “Do What I Mean” says you
should autovivify a Hash if appropriate.
Likewise whether in Perl “$a{$b}++” or ruby “a[b]+=1” the element at a[b]
should be autovivified as 0.
In the case of
a[b]*=c
the only thing that makes sense is if a[b] autovivified as 1
In Ruby, it is not so easy, but clearly “a[b]” doesn’t make sense if
a.kind_of? Array and !b.kind_of?( FixNum), so “Do What I Mean” says you
should autovivify a Hash if appropriate.
But you don’t know what enum is until the method is called, so is your
autovivification code really
going to look like:
if enum[0].kind_of? FixNum
hash = Array.new(0)
else
hash = Hash.new(0)
end
? But you really need to test all the elements on enum, because it
doesn’t have to have all the same
type of elements. Also, Hashes can take FixNums as indicies, so who’s
to say I didn’t actually want
a hash, especially since you were sorting using keys and values later,
so if someone mistakenly passed
in an Enumerable containing only FixNums, your code would break.
Likewise whether in Perl “$a{$b}++” or ruby “a[b]+=1” the element at a[b]
should be autovivified as 0.
In the case of
a[b]*=c
the only thing that makes sense is if a[b] autovivified as 1
Why does only 1 make sense? It would also be fine to say that
a[b] *= c
p a[b] # => 0
is the proper interpretation, in that all FixNums autovivify to have an
initial value of 0, rather than
having a value after assignment of whatever was used.
Really, all these autovivification decisions are arbitrary, and having
variables autovivify depending
on how they’re used would likely require a lot of extra analysis. Isn’t
it more consistant to make
things autovivify always as nil, in cases where that’s required (like
“bar = bar; p bar”, apparently)?