Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Ruby > Pythonic indentation (or: beating a dead horse)

Reply
Thread Tools

Pythonic indentation (or: beating a dead horse)

 
 
Roger Pack
Guest
Posts: n/a
 
      05-28-2009
> #if you actually need an end, that's allowed too
> #(if not further outdented than the line that started the statement)
> result = case ARGV[0]
> when /^\d*$/
> p "That's an integer!"
> when /^[A-Za-z]*$/
> p "That's a word!"
> else
> p "That's something that's not an integer or a word..."
> end if ARGV[0]
>
> #you can explicitly end just the last scope of something deeply nested.
> class A
> class B
> class C
> module D
> foo
> end #actually 4 ends



I assume that that end is optional, too?
end also doubles as the equivalent for Python's "pass", as well?
So you can have ends if you want them but they're optional? Does it
work with {}'s too?
So the basic rule it uses is that if something returns to the original
indentation level without being an end, it assumes it should have been
an end, is that right? (i.e. it *requires* blocks to be at a greater
indentation level).
Thanks just wondering.
-=r
--
Posted via http://www.ruby-forum.com/.

 
Reply With Quote
 
 
 
 
J Haas
Guest
Posts: n/a
 
      05-28-2009
On May 27, 4:32*pm, Caleb Clausen <(E-Mail Removed)> wrote:
> On 5/27/09, Roger Pack <(E-Mail Removed)> wrote:
> >> If you're interested, please see:
> >>http://gist.github.com/117694

>
> > Could you post us a few examples of what code formatted this way looks

>
> Oops, I should have done that when I posted before. Thanks for the reminder.


Fairly awesome. But I think we should consider keeping the colon, and
here's why: blocks that are not delimited by a colon at the start
behave exactly as normal Ruby blocks do, requiring an "end" to close
them, and more importantly ignoring indentation. That way, existing
code can be run through the preprocessor when it's "on", and will
continue to work. And it leaves people to pursue ASCII art with their
indentation, if they are so inclined.

Matz says the colon won't work as a delimiter, though. I'm curious to
know why, but even so, surely something would work.
 
Reply With Quote
 
 
 
 
James Britt
Guest
Posts: n/a
 
      05-28-2009
Reid Thompson wrote:

> I find this code less readable than the above original code with the
> 'end's included


Same here.


Maybe, after enough exposure, I would get used to it, but I don't see
anything intrinsic to that format that would make it more readable, and
currently (for whatever reasons) the endian version is more readable for me.

FWIW, daring people to dispute what is ultimately a matter of opinion is
not terribly productive. I've had enough discussions with Haml lovers
to know that rounds of "But look how beautiful it is" vs "I find it
grating" go nowhere.

Maybe some verifiable proof that compulsory indentation makes for faster
apps or less buggy code or simpler IDE tool development or whatever
would make for a better case.

"I like it" lacks an element of persuasion.



--
James Britt

www.jamesbritt.com - Playing with Better Toys
www.ruby-doc.org - Ruby Help & Documentation
www.rubystuff.com - The Ruby Store for Ruby Stuff
www.neurogami.com - Smart application development

 
Reply With Quote
 
Joshua Ballanco
Guest
Posts: n/a
 
      05-28-2009
On May 27, 2009, at 9:35 AM, J Haas wrote:

> On May 22, 9:01 am, Roger Pack <(E-Mail Removed)> wrote:
>> Tony's point was that certain constructs, like case statements,
>> won't be
>> transformable into indentation only blocks. Does that make sense?

>
> No, it doesn't, because I don't see why case statements are not
> transformable into indent-only blocks. I've _done_ them using the
> quick-and-dirty hacky script and they work just fine. (In cases like
> Joshua's impossible.rb I had to make a minor modification to the
> script to have it inject 'end ' rather than 'end\n', but it still
> worked fine.)


Ok, but now you're beginning to see the edge cases that make this sort
of transformation difficult on a global level. Specifically, how would
you differentiate between these two cases using Pythonic indentation:

> cat conditional_case.rb

result = case ARGV[0]
when /^\d*$/
"That's an integer"
when /^[A-Za-z]*$/
"That looks like a word..."
else
"That's not an integer or a word"
end if ARGV[0]

puts result

> cat case_and_conditional.rb

result = case ARGV[0]
when /^\d*$/
"That's an integer"
when /^[A-Za-z]*$/
"That looks like a word..."
else
"That's not an integer or a word"
end
if ARGV[0]
puts result
end


Attaching a conditional to the end of a case statement (or any block,
for that matter) is possible in Ruby because the block is just an
expression, and the statement doesn't end until the newline. With your
preprocessor, adding "end " when the indentation level decreases would
yield the first form, but then how do I get the second form? I suppose
I could both decrease the indentation level and leave a blank space,
but then it seems like we're adding an awful lot of formatting rules
just to have the code look nice and still function. On top of that, I
think this makes things much less clear! Consider that in "Pythonic
form" the above cases would become:

> cat pythonic_conditional_case.rb

result = case ARGV[0]:
when /^\d*$/
"That's an integer"
when /^[A-Za-z]*$/
"That looks like a word..."
else
"That's not an integer or a word"
if ARGV[0]
puts result

> cat pythonic_case_and_conditional.rb

result = case ARGV[0]:
when /^\d*$/
"That's an integer"
when /^[A-Za-z]*$/
"That looks like a word..."
else
"That's not an integer or a word"

if ARGV[0]:
puts result


Looking at those two pieces of code, is it clear that they do two
different things? At the very least, I think that this sort of change
to the syntax would have to be optional. This would, of course, mean
that you're not getting rid of all of the "end"s in code, so then
what's the point (other than to confuse matters of syntax even more)?

Let me also provide a counter-point. I'm currently using MacVim with
the ruby-vim files and a coloring scheme which colors "def...end"
pairs in yellow and other "case/if/do/etc...end" pairs in purple. This
gives me a good way to quickly look at some code, and if I see that
the "end" immediately preceding a "def" is purple, then I know I've
forgotten and "end" somewhere in that function definition.

Finally, let me suggest a bit of a "wisdom of crowds" argument: if the
advantages to significant whitespace were so unambiguously clear as
you'd have us believe, then why do most languages not use it (even
those developed before many of the advantages of advanced text editors/
IDEs existed)?

- Josh

 
Reply With Quote
 
Jan Friedrich
Guest
Posts: n/a
 
      05-28-2009
J Haas <(E-Mail Removed)> wrote:
> few areas where I think Python beats Ruby, and that's syntatically-
> significant
> indentation.

You think so, I'm not.

> I recognize that
> syntactically-
> significant indentation is not perfect, and it would bring a few pain
> points
> with it. But let me say that again: ONE OUT OF EVERY SIX LINES, for
> crying out
> loud! This should be intolerable to engineers who value elegance.
> "Streaks"
> means what you'd expect: there are four places in the scanned files that
> look
> like this:
>
> end
> end
> end
> end
> end
> end
> end
>
> This is *not* DRY.

Objection! It is DRY: You have already mentioned that in Ruby indention
is not significant. The "ends" in your example are the *only* (not
repeated!) representation of the end of a block.

Regards,
Jan
 
Reply With Quote
 
Reid Thompson
Guest
Posts: n/a
 
      05-28-2009
On Thu, 2009-05-28 at 14:40 +0900, Joshua Ballanco wrote:
> the ruby-vim files


could you provide an url for these?

Thanks,
reid

 
Reply With Quote
 
Juan Zanos
Guest
Posts: n/a
 
      05-28-2009

On May 27, 2009, at 12:35 PM, J Haas wrote:

> On May 22, 9:01 am, Roger Pack <(E-Mail Removed)> wrote:
>> Tony's point was that certain constructs, like case statements,
>> won't be
>> transformable into indentation only blocks. Does that make sense?

>
> No, it doesn't, because I don't see why case statements are not
> transformable into indent-only blocks. I've _done_ them using the
> quick-and-dirty hacky script and they work just fine. (In cases like
> Joshua's impossible.rb I had to make a minor modification to the
> script to have it inject 'end ' rather than 'end\n', but it still
> worked fine.)
>
> Code speaks louder than words, right? Here's some real-world code...
> it's application_controller.rb from the AuthLogic example (http://
> github.com/binarylogic/authlogic_example/tree):
>
> -----------------
>
> # Filters added to this controller apply to all controllers in the
> application.
> # Likewise, all the methods added will be available for all
> controllers.
>
> class ApplicationController < ActionController::Base
> helper :all
> helper_method :current_user_session, :current_user
>
> On May 27, 2009, at 12:35 PM, J Haas wrote:
>
>> On May 22, 9:01 am, Roger Pack <(E-Mail Removed)> wrote:
>>> Tony's point was that certain constructs, like case statements,
>>> won't be
>>> transformable into indentation only blocks. Does that make sense?

>>
>> No, it doesn't, because I don't see why case statements are not
>> transformable into indent-only blocks. I've _done_ them using the
>> quick-and-dirty hacky script and they work just fine. (In cases like
>> Joshua's impossible.rb I had to make a minor modification to the
>> script to have it inject 'end ' rather than 'end\n', but it still
>> worked fine.)
>>
>> Code speaks louder than words, right? Here's some real-world code...
>> it's application_controller.rb from the AuthLogic example (http://
>> github.com/binarylogic/authlogic_example/tree):
>>
>> -----------------
>>
>> # Filters added to this controller apply to all controllers in the
>> application.
>> # Likewise, all the methods added will be available for all
>> controllers.
>>
>> class ApplicationController < ActionController::Base
>> helper :all
>> helper_method :current_user_session, :current_user
>> filter_parameter_logging assword, assword_confirmation
>>
>> private
>> def current_user_session
>> return @current_user_session if defined?(@current_user_session)
>> @current_user_session = UserSession.find
>> end
>>
>> def current_user
>> return @current_user if defined?(@current_user)
>> @current_user = current_user_session &&
>> current_user_session.record
>> end
>>
>> def require_user
>> unless current_user
>> store_location
>> flash[:notice] = "You must be logged in to access this page"
>> redirect_to new_user_session_url
>> return false
>> end
>> end
>>
>> def require_no_user
>> if current_user
>> store_location
>> flash[:notice] = "You must be logged out to access this page"
>> redirect_to account_url
>> return false
>> end
>> end
>>
>> def store_location
>> session[:return_to] = request.request_uri
>> end
>>
>> def redirect_back_or_default(default)
>> redirect_to(session[:return_to] || default)
>> session[:return_to] = nil
>> end
>> end
>>
>> -----------------
>>
>> Nothing particularly special about this code, right? Pretty standard
>> Ruby, if a bit simple? 37 non-blank, non-comment lines, of which 9
>> consist of the bare word "end". I defy anyone to tell me that the
>> code
>> would be less readable as this:
>>
>> -----------------
>>
>> # Filters added to this controller apply to all controllers in the
>> application.
>> # Likewise, all the methods added will be available for all
>> controllers.
>>
>> class ApplicationController < ActionController::Base
>> helper :all
>> helper_method :current_user_session, :current_user
>> filter_parameter_logging assword, assword_confirmation
>>
>> private
>> def current_user_session:
>> return @current_user_session if defined?(@current_user_session)
>> @current_user_session = UserSession.find
>>
>> def current_user:
>> return @current_user if defined?(@current_user)
>> @current_user = current_user_session &&
>> current_user_session.record
>>
>> def require_user:
>> unless current_user:
>> store_location
>> flash[:notice] = "You must be logged in to access this page"
>> redirect_to new_user_session_url
>> return false
>>
>> def require_no_user:
>> if current_user:
>> store_location
>> flash[:notice] = "You must be logged out to access this page"
>> redirect_to account_url
>> return false
>>
>> def store_location:
>> session[:return_to] = request.request_uri
>>
>> def redirect_back_or_default(default):
>> redirect_to(session[:return_to] || default)
>> session[:return_to] = nil
>>
>>

> As this debate unfolds I've watched the stronger criticisms fall
> apart. The
> strongest type of criticism would be that it can't be done, or that
> it's too
> hard to do. But the impossible examples seem to be defeated fairly
> easily.
> Moreover, the solutions are backward compatible to existing Ruby.
>
> Now when I look at this latest example I see some ordinary code
> that's 44 lines
> long. With the pythonic scheme it looks like it's only 35 lines
> long. I find
> it difficult to convince myself that it's a good idea to make code
> 25% larger
> just to preserve some ends of dubious value.
>
> I suppose I could try to come up with some nonsense argument that
> 'end' makes
> everything more readable. But that would just be prejudice. It's
> trivially
> easy to read. It can't just be me. There seems to be no shortage
> of Python
> folk who have no problem either. Objectively, being forced to
> explicitly type
> 'end' all the time seems to takes up a whole lot of space.
>
> filter_parameter_logging assword, assword_confirmation
>
> private
> def current_user_session
> return @current_user_session if defined?(@current_user_session)
> @current_user_session = UserSession.find
> end
>
> def current_user
> return @current_user if defined?(@current_user)
> @current_user = current_user_session &&
> current_user_session.record
> end
>
> def require_user
> unless current_user
> store_location
> flash[:notice] = "You must be logged in to access this page"
> redirect_to new_user_session_url
> return false
> end
> end
>
> def require_no_user
> if current_user
> store_location
> flash[:notice] = "You must be logged out to access this page"
> redirect_to account_url
> return false
> end
> end
>
> def store_location
> session[:return_to] = request.request_uri
> end
>
> def redirect_back_or_default(default)
> redirect_to(session[:return_to] || default)
> session[:return_to] = nil
> end
> end
>
> -----------------
>
> Nothing particularly special about this code, right? Pretty standard
> Ruby, if a bit simple? 37 non-blank, non-comment lines, of which 9
> consist of the bare word "end". I defy anyone to tell me that the code
> would be less readable as this:
>
> -----------------
>
> # Filters added to this controller apply to all controllers in the
> application.
> # Likewise, all the methods added will be available for all
> controllers.
>
> class ApplicationController < ActionController::Base
> helper :all
> helper_method :current_user_session, :current_user
> filter_parameter_logging assword, assword_confirmation
>
> private
> def current_user_session:
> return @current_user_session if defined?(@current_user_session)
> @current_user_session = UserSession.find
>
> def current_user:
> return @current_user if defined?(@current_user)
> @current_user = current_user_session &&
> current_user_session.record
>
> def require_user:
> unless current_user:
> store_location
> flash[:notice] = "You must be logged in to access this page"
> redirect_to new_user_session_url
> return false
>
> def require_no_user:
> if current_user:
> store_location
> flash[:notice] = "You must be logged out to access this page"
> redirect_to account_url
> return false
>
> def store_location:
> session[:return_to] = request.request_uri
>
> def redirect_back_or_default(default):
> redirect_to(session[:return_to] || default)
> session[:return_to] = nil
>
>

As I've watched this debate unfold I've watched the stronger
criticisms fall
apart. The strongest type of criticism would be that it can't be
done, or that
it's too hard to be done. But the impossible examples seem to be
defeated
fairly easily. Moreover, the solutions are backward compatible to
existing
Ruby.

Now when I look at this latest example I see some ordinary code that's
44 lines
long. With the pythonic scheme it looks like it's only 35 lines
long. I find
it difficult to convince myself that it's a good idea to make code 25%
larger
just to preserve some ends of dubious value.

I suppose I could try to come up with some nonsense argument that
'end' makes
everything more readable. But that would just be prejudice. The
pythonic
example is trivially easy to read. It can't just be me. There seems
to be no
shortage of Python folk who have no problem. Objectively, the ends
just take
up a whole lot of space.


 
Reply With Quote
 
Caleb Clausen
Guest
Posts: n/a
 
      05-28-2009
On 5/27/09, Roger Pack <(E-Mail Removed)> wrote:
>> #you can explicitly end just the last scope of something deeply nested.
>> class A
>> class B
>> class C
>> module D
>> foo
>> end #actually 4 ends

>
>
> I assume that that end is optional, too?


Yes.

> end also doubles as the equivalent for Python's "pass", as well?


Mostly. As I understand it, pass in python is a no-op. End 'stand in',
if you will, for the value of the entire statement it's ending.

(In endless.rb, if you really want a no-op to end your statement, you
should use an (appropriately indented) semicolon.)

> So you can have ends if you want them but they're optional?


You got it. Your end now HAS to be intented right, tho.

> Does it work with {}'s too?


No... seemed a little weird to me to have { without the matching }.
But there's no technical reason why { couldn't be autoended too.

> So the basic rule it uses is that if something returns to the original
> indentation level without being an end, it assumes it should have been
> an end, is that right? (i.e. it *requires* blocks to be at a greater
> indentation level).


Yep.

J Hass wrote:
> Fairly awesome. But I think we should consider keeping the colon, and
> here's why: blocks that are not delimited by a colon at the start
> behave exactly as normal Ruby blocks do, requiring an "end" to close
> them, and more importantly ignoring indentation. That way, existing
> code can be run through the preprocessor when it's "on", and will
> continue to work. And it leaves people to pursue ASCII art with their
> indentation, if they are so inclined.


In my preprocessor, end is still allowed, it's just optional now. So
you can copy and paste from a normal ruby file and not worry. (As log
as the snippet you paste is properly indented.)

 
Reply With Quote
 
Eleanor McHugh
Guest
Posts: n/a
 
      05-28-2009
On 28 May 2009, at 15:06, Juan Zanos wrote:
> As I've watched this debate unfold I've watched the stronger
> criticisms fall
> apart. The strongest type of criticism would be that it can't be
> done, or that
> it's too hard to be done. But the impossible examples seem to be
> defeated
> fairly easily. Moreover, the solutions are backward compatible to
> existing
> Ruby.


Some cases have been presented elsewhere in this thread where the
pythonic indentation would fall flat: specifically for expressions of
the form

a = case x
when...
when...
when...
end if y

It's not a common formulation in this form, but it highlights the
problem of using significant whitespace as an expression delimiter in
expression-based languages. Were there a way to solve this ambiguity
elegantly then there would be a case in favour of introducing pythonic
indentation, but until then this would either introduce an unnecessary
limitation on the things that can be expressed in ruby or lead to a
considerably more complicated lexer that might have other implications.

> Now when I look at this latest example I see some ordinary code
> that's 44 lines
> long. With the pythonic scheme it looks like it's only 35 lines
> long. I find
> it difficult to convince myself that it's a good idea to make code
> 25% larger
> just to preserve some ends of dubious value.
>
> I suppose I could try to come up with some nonsense argument that
> 'end' makes
> everything more readable. But that would just be prejudice. The
> pythonic
> example is trivially easy to read. It can't just be me. There
> seems to be no
> shortage of Python folk who have no problem. Objectively, the ends
> just take
> up a whole lot of space.


Begin..end indicates explicitly that a series of expressions is
contained between these statements and that their return value is
available to use as part of a more complex expression which may have
modifiers, something of great value to some of us. Your experience
might well be very different and this might not be a use case you
commonly encounter, but please don't make the mistake of believing
that your use case invalidates the utility of this language feature.
Most of the code I work on takes this form and has perhaps a 10%
density of 'end' statements, if that.

Admittedly when I've worked on Rails apps I've seen the 25%
demonstrated in your quoted example frequently: to my mind that's a
code smell suggesting that much of that code is boilerplate ripe for
abstraction and meta-programming. Such an approach would bring the
'end' density back down to an acceptable and useful level.

The real answer to this issue is to have a pythonic indentation pre-
processor for those who find that of use, and to leave the core
language syntax alone.


Ellie

Eleanor McHugh
Games With Brains
http://slides.games-with-brains.net
----
raise ArgumentError unless @reality.responds_to? :reason


 
Reply With Quote
 
Pascal J. Bourguignon
Guest
Posts: n/a
 
      05-28-2009
Eleanor McHugh <(E-Mail Removed)> writes:

> On 28 May 2009, at 15:06, Juan Zanos wrote:
>> As I've watched this debate unfold I've watched the stronger
>> criticisms fall
>> apart. The strongest type of criticism would be that it can't be
>> done, or that
>> it's too hard to be done. But the impossible examples seem to be
>> defeated
>> fairly easily. Moreover, the solutions are backward compatible to
>> existing
>> Ruby.

>
> Some cases have been presented elsewhere in this thread where the
> pythonic indentation would fall flat: specifically for expressions of
> the form
>
> a = case x
> when...
> when...
> when...
> end if y
>
> It's not a common formulation in this form, but it highlights the
> problem of using significant whitespace as an expression delimiter in
> expression-based languages. Were there a way to solve this ambiguity
> elegantly then there would be a case in favour of introducing pythonic
> indentation, but until then this would either introduce an unnecessary
> limitation on the things that can be expressed in ruby or lead to a
> considerably more complicated lexer that might have other implications.


There's a simply way to solve it. Since everything is an expression,
we can easily wrap expressions and subexpressions in parentheses, and
by the way resolve any ambiguity:

(a = ((case x
when ...
when ...
when ...
end) if y))

((a = (case x
when ...
when ...
when ...
end))
if y)



> Begin..end indicates explicitly that a series of expressions is
> contained between these statements and that their return value is
> available to use as part of a more complex expression which may have
> modifiers, something of great value to some of us. Your experience
> might well be very different and this might not be a use case you
> commonly encounter, but please don't make the mistake of believing
> that your use case invalidates the utility of this language feature.
> Most of the code I work on takes this form and has perhaps a 10%
> density of 'end' statements, if that.


It could be reduced to 0%, if you notice that the close parenthesis is
all that is needed to end the subexpressions.

(a = ((case x
when ...
when ...
when ...) if y))

((a = (case x
when ...
when ...
when ...))
if y)


Also we could decide to always put the operator first, to simplify a little:


(a = (if y
(case x
when ...
when ...
when ...)))

(if y
(a = (case x
when ...
when ...
when ...)))

so it would be clearer what is IF'ed.


> The real answer to this issue is to have a pythonic indentation pre-
> processor for those who find that of use, and to leave the core
> language syntax alone.


Exactly. The concrete syntax should be purely a question of
presentation and user interface, that should be dealt with by the
editor. Source files could be kept in s-exp form, like it was
intended originally (50 years ago).

--
__Pascal Bourguignon__
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Re: Beating my head -- JSPs and encoding Hegemony Cricket Java 1 08-22-2012 01:29 PM
remove overall indentation preserving reletive indentation Jesse B. Ruby 2 03-27-2010 07:23 PM
Object Oriented vs Pythonic Code, and Pythonic standards Carl J. Van Arsdall Python 4 02-07-2006 10:15 PM
Learning and beating my head Brian Andrus Java 2 05-04-2004 07:38 AM
anyone using an iPod probably deserves a beating Lucas Tam Digital Photography 38 04-29-2004 02:01 PM



Advertisments