Pythonic indentation (or: beating a dead horse)

J

Juan Zanos

wellwhydontwegetridofallpunctuationthingslikespacescommassemicolonsquotesetcperiodcertainlytakeupmuchmoreofourprosethantheydeserveandcapitalizationeatsupvaluableverticalspaceandnoneedforparagraphseparationeither

Seriously, if you measure things by avoiding extra keystrokes, get a
better editor. I value readability over parsimony of lexical items.



--
Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Twitter: http://twitter.com/RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale

That would be a valid criticism if J Haas's suggestion made things
less readable. But having to scroll less makes things more readable.
 
J

Joshua Ballanco

My friends, when ONE OUT OF EVERY SIX of your code lines consists of
just the
word "end", you have a problem with conciseness. 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. Or anything remotely resembling it. This is an
ugly blemidh on a language that otherwise is very beautiful.

It's at this point that I started to wonder if you've had a look-see
at _why's new language, Potion? Specifically:
 
R

Roger Pack

...maybe something like this:
module Kernel
override print(*args)
do_something
overridden *(args + [" :)"])
end
end

Yeah the inheritance chain thing is hard.
The closest you can come is with modules.

class Class
def override &block
m = Module.new
m.module_eval &block
include m
end
end

# now

class Object
override do
def print *args
super *(args + [" :)"])
end
end
end
3 :)

ref: http://www.ruby-forum.com/topic/176080#new last post
http://yehudakatz.com/2009/03/06/alias_method_chain-in-models/

But I digress... the purpose of this post is to talk about one of the
relatively
few areas where I think Python beats Ruby, and that's syntatically-
significant
indentation.

Not having the "end" is indeed very clean and kind on the eyes--kind of
reminds me of what Ruby does, which is try to make things look better
for you.

The only complaint I've heard about it was from Tony Arcieri's post
wondering "how do you return anything from blocks?"

i.e. in Ruby "array.collect{|a|}.reject{|b| true}"

how to do that with indented blocks.

That being said, splitting it up into 3 neat python blocks doesn't look
bad at all, so it's not a big loss there. I'm not too familiar with
Python syntax so I'm not exactly sure what's possible, either. What
would a good syntax be to return values from indented blocks?
My proposal is to, first, not change a thing with respect to existing
syntax. Second, steal the : from Python and use it to signify a scope
that's marked by indentation:

I did note Matz recent comment on using the ":" as a separator:
http://redmine.ruby-lang.org/issues/show/1389

Perhaps you could take your pitch to him :)
while some_condition:
# this scope will terminate when the indentation level decreases
to the
# level before it was entered
do_something

Interesting--so you propose to use : when you want a block but *dont*
care about using its return value, is that right?

We can get around this by saying that braces, wherever they may appear,
always define a new scope nested within the current scope, regardless
of indentation.

def do_something(a, b, c):
{ print a, b, c } # this works
a + b + c

and then afterward it picks up the previous indentation? I
suppose...that would work. It's an odd use for {}'s though which are
already pretty heavily used.

Overall I like the idea--making "end's" optional would indeed be kind.
It is better than a similar idea I had recently, which was to use : for
single line blocks (only), i.e.

if a == b : do_something
instead of
if a == b; do_something; end

but your suggestion seems to include even more than that.

The other concern is that "the allure of magic indentation wears thin
for large docs" which fact "worked to ensure you kept your methods
short."[1]

Also could parsers handle it?
Thoughts?

=-r
[1] http://www.artima.com/forums/flat.jsp?forum=123&thread=256682
 
J

J Haas

Seriously, if you measure things by avoiding extra keystrokes, get a
better editor.  I value readability over parsimony of lexical items.

Cluttering up your code with "end" everywhere makes it less readable,
not more.

I have some suspicion this was a joke thread.
(Hint: Format of OP)

Nope, I'm sincere. As I said before, I'm new. The formatting was a
misguided attempt to make things look nice by manually wrapping them
at 80 columns. Oops.
This sort of structuring pieces of code stopped me from trying Python
seriously.

And I specifically addressed this in my OP. A lot of engineers who
haven't done work in Python think that the idea of giving syntactical
significance to whitespace is a ridiculous idea. I used to be one of
them. Now I know better. Don't knock it until you've tried it.
Isn't there an Syntax-Highlighting Editor out there which allows assigning
1-Point size to End-Only-Lines?

Aside from the fact that this wouldn't solve the problem that I'd
still have to _write_ the damned things, this is possibly the
kludgiest solution imaginable.
Wouldnt this be the easiest way to solve your problem?

The easiest way to solve _my_ problem would be for me to use one of
the preprocessing scripts that impose Python-like indentation syntax
on Ruby. But that wouldn't solve the larger problem, which is that
Ruby could be better than it is.

It's at this point that I started to wonder if you've had a look-see
at _why's new language, Potion?

Nope. but I'll check it out. Thanks.
 
R

Rick DeNatale

Cluttering up your code with "end" everywhere makes it less readable,
not more.

Honestly, that's subjective. Some people prefer delimiters some don't.

In general I'm not a fan of languages whose syntax depends on the
number of rather than just the presence of whitespace characters. I
find it hard to count blanks while reading.

I tried hard to like haml for instance. It has a lot of good points,
but the pythonic style of nesting just doesn't work for me.

In code, the real solution is not to nest so deeply that it becomes a
problem, learn to use the composed method pattern.



--=20
Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Twitter: http://twitter.com/RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale
 
J

J Haas

Yeah the inheritance chain thing is hard.
The closest you can come is with modules.
...
class Object
 override do
   def print *args
     super *(args + [" :)"])
   end
 end
end

3 :)

Nifty! Thanks.
The only complaint I've heard about it was from Tony Arcieri's post
wondering "how do you return anything from blocks?"

i.e. in Ruby "array.collect{|a|}.reject{|b| true}"

how to do that with indented blocks.

I still have a tough time understanding this objection. What I'm
essentially proposing is that at least in the case of code blocks that
start with a colon, a de-dent takes the place of an end. Given that, I
just don't see what it is that could be done without end that couldn't
be done without dedenting. In this example, the braces would continue
to work as expected (and there are no end statements), but for
something like this:

array.reject do |b|
true
end

.... it would just be:

array.reject do |b|:
true

I guess with the collect thrown in, the current implementation with do-
end rather than braces would be

array.collect do |a|
end.reject do |b|
true
end

...is that right? Using indentation to deliniate blocks does have the
problem that you can't have a null block like the one passed to
collect here. Python gets around it with the keyword "pass", as in:

for x in xrange(50): # Python equivalent of (0...50).each do |x|
end
pass

So this could be:
arra.collect do |a|:
pass
..reject do |b|:
true
Interesting--so you propose to use : when you want a block but *dont*
care about using its return value, is that right?

No, not exactly. The issue with return values is orthogonal to this.
My proposal is to use the : if you want dedent to substitute for end
to terminate the block. I still don't see why return values are
affected.
and then afterward it picks up the previous indentation? I
suppose...that would work.  It's an odd use for {}'s though which are
already pretty heavily used.

True, and I'm not too thrilled with the solution, but maybe there's
something better. {} is heavily used, but on the other hand one of its
common uses is to divide code into blocks, and that's what it's doing
here.
Overall I like the idea--making "end's" optional would indeed be kind.
It is better than a similar idea I had recently, which was to use : for
single line blocks (only), i.e.

if a == b : do_something
instead of
if a == b; do_something; end

This suggestion is not at all incompatible with mine, and in fact
that's the way Python does it too (except you'd have to put parens
after do_something :)...

if a==b:
# Nothing follows the colon, so this starts a new scope indented
past the if statement
do_something()
do_something_else()

if a==b: do_something() # code follows the colon, so it constitutes
the entire block
do_something_else()

if a==b:
do_something_else() # error, the parser expects a new level of
indentation

if a==b: do_something()
do_something_else() # error, the parser does not expect a deeper
indent
The other concern is that "the allure of magic indentation wears thin
for large docs" which fact "worked to ensure you kept your methods
short."[1]

Well, all I can say is, that's his opinion. Hasn't lost its allure for
me. And if it did lose its allure, I would think that it wouldn't be
for large docs, it would be for deeply _nested_ docs. I definitely
sympathize with his mockery of the notion that this limitation is a
blessing in disguise because it encourages you to keep your methods
short... it reminds me of how I felt when I first heard that Java's
lack of pointers meant you couldn't write any pointer bugs. But my
employer imposes a strict 80-column limit on source files including
Python, we have a lot of Python code, and it all works out.
Also could parsers handle it?

I think so, especially since the preprocessor I've used (wish I could
remember its name... it's not Lazibi [1]) does it in a fairly clever
way in Ruby itself, so I don't see why the parser couldn't do what it
does.

Thanks for your comments, I appreciate the feedback.

[1] http://lazibi.rubyforge.org/
 
T

Tony Arcieri

[Note: parts of this message were removed to make it a legal post.]

I still have a tough time understanding this objection. What I'm
essentially proposing is that at least in the case of code blocks that
start with a colon, a de-dent takes the place of an end. Given that, I
just don't see what it is that could be done without end that couldn't
be done without dedenting.


I already responded to this. I guess you didn't see it. There are several
types of statements which contain multiple indent blocks (and thus multiple
dedent tokens). These would include if statements:

if foo
blah
elsif bar
blah2
else
baz

Begin statements (ala try/catch in Python)

begin
somecode
morecode
rescue FooError
some_rescue_action
rescue BarError
another_rescue_action
ensure
something_gets_done

Case statements:

case foo
when bar
do_something
when baz
do_something_else

Each of these statements is an expression with multiple clauses. How do you
tell when these expressions are complete? Obviously a naive "dedent = end
of expression" approach doesn't work in these cases.

These work in Python because Python has a special grammar for statements
which doesn't require a statement separator for any statements which have
indent blocks. This doesn't work in a language where everything is an
expression, because all expressions must be treated the same and all
expressions must have an expression terminator (newline or semicolon)
 
T

Tony Arcieri

[Note: parts of this message were removed to make it a legal post.]

I already responded to this. I guess you didn't see it. There are several
types of statements which contain multiple indent blocks (and thus multiple
dedent tokens). These would include if statements: [... snip ...]

Begin statements (ala try/catch in Python) [... snip ...]

Case statements: [... snip ...]

case foo
when bar
do_something
when baz
do_something_else

And yet another consideration with this in Ruby vs. Python: in Ruby these
are all expressions (as has been stated repeatedly) and therefore you can do
this sort of thing:

x = case foo
when bar
do_something
when baz
do_something_else
else
whatever
end

i.e. the case statement returns the value of the last expression evaluated
in the taken branch.

This is not the case in Python (well first because Python doesn't have case
statements) where no expressions can contain indent blocks.
 
J

Juan Zanos

Honestly, that's subjective. Some people prefer delimiters some don't.

Is it subjective? Neither method is ambiguous. So no problem
there. But scrolling 16% more often? That must have some cost to
readability.
 
J

Joshua Ballanco

Also could parsers handle it?

I think so, especially since the preprocessor I've used (wish I could
remember its name... it's not Lazibi [1]) does it in a fairly clever
way in Ruby itself, so I don't see why the parser couldn't do what it
does.

A friendly suggestion: One of Ruby's strengths (one that it shares
with Perl) is its power to manipulate and parse strings. If you're new
to Ruby, a script which takes your colon/de-indent syntax and turns it
into the proper do/end syntax sounds like a great project to get
started. Since you desire to write Ruby code in this way, it would
also give you a great excuse to approach this problem using TDD. That
is, write the code in the way you would like it to appear as a test,
then write the parser/de-re-mangler to have the test pass. I honestly
think you could have done this with the same amount of effort as it
has taken to reply to each of the e-mails in this thread.

Let me be frank: Your suggestion is not new. Code speaks louder than
words. When you _do_ implement this alternative Ruby syntax, I think
you may be surprised at the number of corner and edge cases. Ruby is
currently in the process of being standardized. I would imagine the
last thing that Ruby needs while undergoing the standardization
process is a new, optional syntax with a multitude of corner and edge
cases.

Cheers,

Josh
 
G

Gregory Brown

Is it subjective? =A0 Neither method is ambiguous. =A0So no problem there= =A0But
=A0scrolling 16% more often? =A0 That must have some cost to readability.

If you break up code into more files, or use a folding editor, or use
an editor that lets you jump to code by a method name, scrolling is a
non-issue.

Rick's point remains. If this is an issue you're facing, get a better edit=
or.

-greg
 
R

Robert Klemme

I am not a zealot and have little tolerance for zealotry, and I have
no
desire to get involved in holy wars.

The length of your statement and the wording seem to indicate differently:
hate that
Ruby doesn't have it.
My friends, when ONE OUT OF EVERY SIX of your code lines consists of
just the
word "end", you have a problem with conciseness.

(So far I can only see that you are having a problem.)
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.
Well, tough. Programmers shouldn't be using freakin' tabs anyway, and
I don't think
it's worthwhile to inflate the code base by a staggering 20% to
accommodate
people who want to write ugly code,

Kind regards

robert
 
M

Martin DeMello

Honestly, that's subjective. Some people prefer delimiters some don't.

Scheme strikes a beautiful balance, I think

(class Object
(def initialize foo bar
(whatever
whatever)))

no strings of ends, but no significant whitespace, and vim users can
bounce on the % key and be happy.

martin
 
J

J Haas

I already responded to this.  I guess you didn't see it.

I responded to your response, I guess you didn't see _that_. Tell me
what's wrong with the following alternatives to your examples:
if foo
  blah
elsif bar
  blah2
else
  baz

Just like with your case example before, whoops! You're missing an
end, and the parser will complain when it sees the end-of-file without
a missing end. And just as I said with your case example before, I
sure hope this missing end isn't in the middle of a long file or you
get to spend a lot of time scrolling through your code looking for the
place where it's missing.

But if we amend your example to

if foo
blah
elsif bar
blah2
else
bar
end

....I really don't see what's wrong with this as an alternative:

if foo:
blah
elsif bar:
blah
else:
bar

No end necessary.
Begin statements (ala try/catch in Python)

begin
  somecode
  morecode
rescue FooError
  some_rescue_action
rescue BarError
  another_rescue_action
ensure
  something_gets_done

Again, you're missing an end. Applying the same pattern I applied in
the if and case examples, I do not see what the problem is. How is it
any less functional than the current Ruby?
Each of these statements is an expression with multiple clauses.  How do you
tell when these expressions are complete?  Obviously a naive "dedent = end
of expression" approach doesn't work in these cases.

Okay, I think I see what you're saying now. But each of those also, in
the clauses after the first one, begins with a keyword that is not
valid except as part of the previous expression. Does it solve the
problem if dedenting ends an expression unless it's immediately
followed by a keyword that makes no sense except as a continuation of
the expression?
This doesn't work in a language where everything is an
expression, because all expressions must be treated the same and all
expressions must have an expression terminator (newline or semicolon)

You keep on saying this, yet I have not seen one example of code that
demonstrates that using dedent couldn't work. Basically, we're looking
for some code which, if you removed the all ends, a script following
simple deterministic rules would not be able to unambigiously decide
where to put them back. I agree that "put an end wherever you see a
dedent" is naive and wouldn't work... but "put an end wherever you see
a dedent which isn't followed by a keyword which extends the current
expression, such as elsif or rescue or ensure" might.
 
P

pat eyler

And I specifically addressed this in my OP. A lot of engineers who
haven't done work in Python think that the idea of giving syntactical
significance to whitespace is a ridiculous idea. I used to be one of
them. Now I know better. Don't knock it until you've tried it.


And you're on a mailing list full of people who find that the Ruby-way
makes sense to them. Don't knock it until you've tried it.
The easiest way to solve _my_ problem would be for me to use one of
the preprocessing scripts that impose Python-like indentation syntax
on Ruby. But that wouldn't solve the larger problem, which is that
Ruby could be better than it is.

Or not. Beauty is in the eye of the beholder. Thinking that the new
(to you) language that you're working with today would be better if
it were more like your old language is a big reason that so many
people write [perl|C|fortran] code in so many different languages.


--=20
thanks,
-pate
 
J

Juan Zanos

If you break up code into more files, or use a folding editor, or use
an editor that lets you jump to code by a method name, scrolling is a
non-issue.

Rick's point remains. If this is an issue you're facing, get a
better editor.

-greg


You want me to have even more files and spend even more time folding
and unfolding things. And you assume I use a crummy editor and there
is a better one that magically makes a surprising number of wasted
lines irrelevant. I think you accidentally illustrated that extra
lines have a cost.

I can understand that people grow attached to certain styles. That's
no big deal. I really like Ruby a lot. I like it better than
Python. I believe in most ways it's a better language. But I'm
willing to admit that Python programs are generally shorter and that
is an advantage. It's silly to pretend it isn't.

Maybe it's too hard to get rid of the redundancy. Or maybe we just
don't know how yet. I'm not sure. But let us at least admit it's
there. We should give people credit for trying to solve the problem.
 
R

Ryo Furue

If you break up code into more files, or use a folding editor, or use
an editor that lets you jump to code by a method name, scrolling is a
non-issue.

Rick's point remains.  If this is an issue you're facing, get a better editor.

But, the amount of meaningful code displayed in a single screenshot
still suffers. A screenful is a chunk of code
you can see without scrolling or jumping. The more meaning it
contains, the better you understand the code (as long as the meaning
is cleanly laid out, of course).

The point, I think, is what you gain by having "end"s
and what you lose. Since the gain is much smaller ON THE
PROGRAMMERS' PART than the loss, it's better NOT to have
"end"s. The lack of "end"s may be a headache for compiler writers.
I don't know if it's the case.

I have the same opinion as the OP, from my tiny experience with
Haskell. After only an hour or so of Haskell programming, I already
appreciated the blessing of not being forced to add
an extra line just to close a block. An "end" line doesn't add any
meaning but takes up a line.

Regards,
Ryo
 
G

Gregory Brown

You want me to have even more files and spend even more time folding and
unfolding things. =A0And you assume I use a crummy editor and there is a
better one that magically makes a surprising number of wasted lines
irrelevant. =A0I think you accidentally illustrated that extra lines have= a
cost.

I didn't accidentally illustrate anything. And everything has a
cost. Why don't you list for yourself the cost of editing code with
significant white space?
Maybe for you, those costs are irrelevant. For me, the costs of ends
in Ruby code are irrelevant.

But your point was about *readability* not maintainability. I don't
think that good project organization is a 'cost' to readability.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,051
Latest member
CarleyMcCr

Latest Threads

Top