On Nov 13, 2007, at 12:10 AM, furtive.clown / gmail.com wrote:

> I forgot to include this option:
>
>    t = stems.map { |f|
>       f + "." + guess_extension(f)
>    }
>    all_files3 = transform2(source_files + transform1(t)).map { |f|
>       File.join(dir, f)
>    }
>
> Introducing needless temporaries on the same scope level as the target
> variable makes understanding and maintenance harder.  Look again:
>
>    all_files = stems.map { |f|
>       f + "." + guess_extension(f)
>    }.as { |t|
>       transform2(source_files + transform1(t))
>    }.map { |f|
>       File.join(dir, f)
>    }
>

the very real problem with this sort of thing is that exceptions will  
map to one fugly line, making debugging for some poor soul a  
nightmare.  imagine that 'guess_extension' returned nil, or  
'transform2' for that matter.  this kind of golfing is great for  
perl, but i've found over many painful experiences that it offers  
little in the way of clarity or maintainability in production code.   
i think at least a few ruby hackers, hardened by sunday night  
debugging sessions, and anyone fleeing perl, will agree that  
something along the lines of

   guesses = stems.map{|stem| "#{ stem }.#{ guess_extension stem }"}
   basenames = transform2 source_files + transform1(guesses)
   expanded = basenames.map{|basename| File.join dir basename}

is clear, maintainable, and easy to understand even if you don't  
dream of lambda calculus.  come back six months later and add  
transform3 and you'll appreciated what i'm talking about

and of course this isn't even addressing the issue that anyone  
*really* naming a function transform1 or transform2 should be  
smothered in their sleep, nor the fact that such functionality  
should, in something as beautiful as ruby, end up looking like

   filelist = Filelist.for source_files, :stems => stems, :dir => dir

making the pimpl nearly *irrelevant* so long as it's clear and allows  
easy debugging and testing - testing being another *huge* checkmark  
against monolithic method chains.

> This clearly communicates that the purpose of the block chain is to
> obtain a value for all_files.  From beginning to end, we understand
> the intent at a glance.  In the former case, the intent is muddled by
> a useless variable floating around.  Multiply this example by 10 and
> it quickly becomes the difference between beauty and travesty.

i strongly disagree: variable names are one of the single most  
important elements of writing maintainable code - unless you happen  
to be one of the very few who loves writing documentation (i  
certainly don't).  variables being references in ruby, i consider it  
something almost evil *not* to through in a well named variable where  
it lends clarity by literally spelling out the programmer's intent,  
cuts line length, makes transitioning or modifying the code later  
vastly easier, and stacktraces actually meaningful

none of this is to say that an Object#as or Object#tap is a bad idea  
or that it always makes things less clear.  but it's really a stretch  
to say it makes the above logic clear.  i used to think

   for(i = j = k = 0; i < irank(matrix) && j < jrank(matrix) && krank 
(matrix); i++, j++, k++){
     ...

was clever, and now it makes me want to run screaming into the woods  
yelling 'internal iterator god damn it!'

so my concern with #as and #tap is that the lend strong support to  
bad programming practices - making what should be hidden and internal  
exposed and external.

kind regards.

a @ http://codeforpeople.com/
--
it is not enough to be compassionate.  you must act.
h.h. the 14th dalai lama