Pythonic indentation (or: beating a dead horse)

And here's what exacerbates the problem in a CFG: in a grammar like Ruby,
things like case statements, if statements, and method calls with blocks are
among what I believe is the lowest precedence set of operations. In Python,
statements with indent blocks are among the highest precedence operations.

So we can do things where expressions with indent blocks need to be lower
precedence, and that's where the proverbial shit really starts to hit the
fan. How would you parse this, for example?

n = 2 * -[1,2,3,4,5].inject(0) do |a, b|
  c = a * 2
  a * c + b
result = 8 * -if n > 0
  -n += 10000
else
  n + 500000
puts result

And just as a matter of personal preference, I find the above with end
statements more readable:

n = 2 * -[1,2,3,4,5].inject(0) do |a, b|
  c = a * 2
  a * c + b
end
result = 8 * -if n > 0
  -n += 10000
else
  n + 500000
end
puts result

···

On Wed, May 20, 2009 at 2:45 PM, Tony Arcieri <tony@medioh.com> wrote:

I'd be particularly interested in solutions which can be parsed with a
LALR,
LL*, or PEG parser. The approach you're describing does not sound like it
could be expressed as a CFG, not that there isn't a CFG solution for this,
just that the one you're proposing is not.

--
Tony Arcieri
medioh.com

Okay. But just to recap what's gone before: you've said all along that
Pythonic indentation in Ruby was impossible. It's impossible, because
it's incompatible with blocks. It's impossible, because everything's
an expression. It's impossible, because Guido said so. And I've asked
you over and over for an example demonstrating its impossibility.
You've had time to think it over, you were under no constraints, and
this is what you've come up with.

I found the old preprocessor script from awhile ago that I mentioned
earlier in this thread. I've pastebinned it at http://pastebin.com/m5fee1e92\.
It's very short, and pretty simple. I have no doubt that it would need
a lot of work to be robust. (In particular, I think things need to be
added to the enumeration at line 68.)

But Tony, it blasted through your "impossible" example without
skipping a beat, working perfectly on the first try.

jhaas@littlefinger:~/pyruby$ cat pyrbtest.rb
__BEGIN__

# Look, ma! No ends!

foo = [1,2,3]
x = foo.map do |n|:
  n += 1
  n *= 2

result = case x:
  when Array
    x.map! do |n|:
      n *= 3
      n += 4
  when NilClass
    x

result = if result.size > 10:
  result[0..2]
else:
  result

p result
jhaas@littlefinger:~/pyruby$ irb
irb(main):001:0> require 'pyruby'
=> true
irb(main):002:0> require 'pyrbtest'
[16, 22, 28]
=> true

As I've said repeatedly, I don't follow your reasons for claiming that
this is impossible. Can you come up with a better demonstration?

···

On May 20, 1:45 pm, Tony Arcieri <t...@medioh.com> wrote:

Okay, you just want an example I guess. Code speaks louder than words!

Here is an example Ruby program:

foo = [1,2,3]
x = foo.map do |n|
n += 1
n *= 2
end
result = case x
when Array
x.map! do |n|
n *= 3
n += 4
end
when NilClass
x
end
result = if result.size > 10
result[0..2]
else
result
end
p result

The output of this program is:

[16, 22, 28]

Show me how you would parse the equivalent program in an
indentation-sensitive Ruby, e.g.:

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.

···

On Wed, May 20, 2009 at 2:35 PM, J Haas <Myrdred@gmail.com> wrote:

On May 20, 8:51 am, Rick DeNatale <rick.denat...@gmail.com> wrote:

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.

--
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 Haas:

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.

I’m one of the idealists who actually loved Python’s idea of
indentation, meaningful whitespace and the least possible clutter
(to the point that I actually preferred tabs to spaces – that’s the
whole point of a tab, to *indent*), and I did write quite a few lines
of Python in my life – and after all that, I’m now strongly in the
‘no significant whitespace’ camp.

I might miss my idealism, but the practical advantages of
non-significant whitespace win for me on all fronts: I can
actually use code snippets without reindenting them, badly-indented
code tells me outright that I shoudn’t even bother reading it, and
I can do all kinds of commenting-out/debug print calls really fast.

Aside from the fact that this wouldn't solve the problem
that I'd still have to _write_ the damned things

If you have to write the ‘end’ statements yourself (and it bothers you),
then you either don’t use a sane editor or didn’t configure it properly.

— Shot

···

--
A causal stroll throught lunatic asylum
shows that faith does not prove anything.

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.

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.

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.

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.

···

On Wed, May 20, 2009 at 12:35 PM, J Haas <Myrdred@gmail.com> wrote:

On May 20, 8:51 am, Rick DeNatale <rick.denat...@gmail.com> wrote:

--
thanks,
-pate
-------------------------
Don't judge those who choose to sin differently than you do

I'd be particularly interested in solutions which can be parsed with a
LALR,
LL*, or PEG parser. The approach you're describing does not sound like it
could be expressed as a CFG, not that there isn't a CFG solution for this,
just that the one you're proposing is not.

And I guess I just can't shut up about this: your solution would require a
scanner with some degree of grammatical awareness for every expression which
contains an indent block. When you start talking about a solution like
that, I really think you're going down a rabbit hole.

I'll refer to Guido van Rossum on this one:

The unspoken, right brain <http://en.wikipedia.org/wiki/Right_brain&gt;constraint here is that the complexity introduced by a solution to a design
problem must be somehow proportional to the problem's importance. In my
mind, the inability of lambda to contain a print statement or a while-loop
etc. is only a minor flaw; after all instead of a lambda you can just use a
named function nested in the current scope.

But the complexity of any proposed solution for this puzzle is immense, to
me: it requires the parser (or more precisely, the lexer) to be able to
switch back and forth between indent-sensitive and indent-insensitive modes,
keeping a stack of previous modes and indentation level. Technically that
can all be solved (there's already a stack of indentation levels that could
be generalized). But none of that takes away my gut feeling that it is all
an elaborate Rube Goldberg contraption<http://www.rube-goldberg.com/html/gallery.htm&gt;
.

You are proposing a Rube Goldberg contraption whereby the scanner would
require some degree of grammatical awareness. This is a complex solution.
The minor flaw is that you don't like "end" statements. Is the minor flaw
of having repeated end statements really worth the level of complexity the
solution would require?

···

On Wed, May 20, 2009 at 4:18 PM, Tony Arcieri <tony@medioh.com> wrote:

--
Tony Arcieri
medioh.com

__BEGIN__

n = 2 * -[1,2,3,4,5].inject(0) do |a, b|:
  c = a * 2
  a * c + b
result = 8 * -if n > 0:
  -n += 10000
else:
  n + 500000
puts result

Once again, worked perfectly on its first time through the
preprocessor.

···

On May 20, 3:18 pm, Tony Arcieri <t...@medioh.com> wrote:

n = 2 * -[1,2,3,4,5].inject(0) do |a, b|
c = a * 2
a * c + b
end
result = 8 * -if n > 0
-n += 10000
else
n + 500000
end
puts result

Sorry for the repeated posts, but I forgot to mention: I did not write
this, although I did make minor modifications. I don't recall where I
found it, and Google was no help. I regret that I cannot credit the
author; should he happen to be reading this, speak up!

···

On May 21, 9:16 am, J Haas <Myrd...@gmail.com> wrote:

I found the old preprocessor script from awhile ago that I mentioned
earlier in this thread. I've pastebinned it athttp://pastebin.com/m5fee1e92.

Okay. But just to recap what's gone before: you've said all along that
Pythonic indentation in Ruby was impossible. It's impossible, because
it's incompatible with blocks. It's impossible, because everything's
an expression. It's impossible, because Guido said so. And I've asked
you over and over for an example demonstrating its impossibility.
You've had time to think it over, you were under no constraints, and
this is what you've come up with.

My claims were it's impossible with a Pythonic lexer and a backing context
free grammar. I certainly didn't claim that if you throw enough regexes at
the problem it won't go away.

I found the old preprocessor script from awhile ago that I mentioned
earlier in this thread. I've pastebinned it at
http://pastebin.com/m5fee1e92\.
It's very short, and pretty simple. I have no doubt that it would need
a lot of work to be robust. (In particular, I think things need to be
added to the enumeration at line 68.)

But Tony, it blasted through your "impossible" example without
skipping a beat, working perfectly on the first try.

Well great! I'm not really sure how that script works, but cool. However,
what you have there is a marked departure from how Python actually works.
But if you're happy with it, great. Go for it and see how popular you can
make it.

···

On Thu, May 21, 2009 at 10:27 PM, J Haas <Myrdred@gmail.com> wrote:

--
Tony Arcieri
medioh.com

And sorry to be frank about this: I've been cordial and fact oriented in
this discussion, trying to explain my opinion as best as possible. Not only
are you strawmanning me here, you're being a right cunt. Perhaps you could
try to express your ideas without making it personal?

···

On Thu, May 21, 2009 at 10:27 PM, J Haas <Myrdred@gmail.com> wrote:

Okay. But just to recap what's gone before: you've said all along that
Pythonic indentation in Ruby was impossible. It's impossible, because
it's incompatible with blocks. It's impossible, because everything's
an expression. It's impossible, because Guido said so. And I've asked
you over and over for an example demonstrating its impossibility.
You've had time to think it over, you were under no constraints, and
this is what you've come up with.

--
Tony Arcieri
medioh.com

As I've said repeatedly, I don't follow your reasons for claiming that
this is impossible. Can you come up with a better demonstration?

First, let me say thank you for the code. This community thrives on code, not on bickering. More code is always a good thing. That said...

> cat tough.rb
# "end" can make things clearer, sometimes...

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

puts result

···

On May 22, 2009, at 12:27 AM, J Haas wrote:

"J Haas" <Myrdred@gmail.com> schrieb im Newsbeitrag news:3dd501b0-84bc-4781-af21-f09714148e6b@y6g2000prf.googlegroups.com...

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.

I've used many languages in team projects, for example Assembler, Fortran, Modula, C, C++, Pascal, Ruby, Batch, bash, TCL.
The only thing common to these projects: Nobody seems to be able to use formatting consistently on different platforms.

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.

So just use a macro which replaces CTRL-BS by end.

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.

You will additionally need preprocessing for importing code.

But that wouldn't solve the larger problem, which is that
Ruby could be better than it is.

Then it would not be your problem anymore, but the problems of tons of other Rubyists. Which, at current time, probably have absolutely no problem using end or "}".
(BTW: For at least 10 years, I've never read or heard about this suggestion for Ruby.)

Are you really sure you are no FUD-guy paid by MS which tries to
spoil other scripting languages in favor of F#?

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

···

On May 20, 2009, at 2:51 PM, Rick DeNatale wrote:

On Wed, May 20, 2009 at 2:35 PM, J Haas <Myrdred@gmail.com> wrote:

On May 20, 8:51 am, Rick DeNatale <rick.denat...@gmail.com> wrote:

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.

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

···

On Thu, May 21, 2009 at 12:21 AM, Rick DeNatale <rick.denatale@gmail.com> wrote:

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

Just to kick in on this, I am a long-time Ruby programmer and Ruby is my favorite language. I'd rather see full-strength macros than optional removal of 'end' keywords. Having said that, let me admit I do not know how complex it would be to implement a solution in Ruby that optionally omits ends. Some have argued, with what seem like plausible reasons, that it might be hard. Others have attempted to rebut those reasons. I agree with the calculus that if it is very difficult, it is not worth the benefit. If it is easy, I'd like to be able to omit the 'end' statements. They are line noise to me visually. They just get in the way.

Another concern I have about this idea, though, is having essentially two forks of Ruby lexically speaking. It's not much of an obstacle, but it is a minor difference that someone who comes along later to maintain your code might find confusing or off-putting. And even though this particular change might be relatively minor, where do we draw the line? What makes us decide whether another optional syntax change should be integrated into the language? Even many small changes add up to a big difference in the way the code looks and the ease of maintainability.

I guess, however, this is no more serious than the positioning-of-brackets debate in C or even whether bracketless 'if' statements should ever be used, e.g.:

     if (foo)
         bar();
         baz();

The 'baz' function will execute regardless of the value of foo; this code is therefore visually confusing.

Macros, as I suggested above, would _really_ move in the direction of potential Ruby code that looks radically different. But the payoff would be enormous expressive power, the ability to mold the language to suit the application domain in a very comprehensive way. The removal of 'end', by contrast, would be only a slight improvement. It would remove some visual noise and might attract a few Python programmers. As I said, I think it's worth it if it's easy to implement, but not if it's more difficult.

steven

···

On May 20, 2009, at 4:31 PM, pat eyler wrote:

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.

I'll refer to Guido van Rossum on this one:

The unspoken, right brain Lateralization of brain function - Wikipedia
constraint here is that the complexity introduced by a solution to a design
problem must be somehow proportional to the problem's importance. In my
mind, the inability of lambda to contain a print statement or a while-loop
etc. is only a minor flaw; after all instead of a lambda you can just use a
named function nested in the current scope.

I think this helps illustrate the degree of subjectivity
involved in how varying people regard this issue.

For me, I've written sufficient Python code in years past
to have concluded that significant indentation adds too
much rigidity to the language for it to feel like an
acceptable trade-off for what it accomplishes.
(The mere existence of the 'pass' keyword is slightly
eye-rolling to me.... presumably similar to how others
may feel about a raft of closing 'end' statments.)

My preferences are opposite to Guido's, above. A flexible
lambda, for instance, is *far* more important to me than
what I consider to be a minor nuisance of nested 'end'
statements. (And non-nested 'end' statements don't bother
me at all. Some percentage of extra scrolling in the
editor isn't of any relative importance to me.)

As far as nested 'end' statements, one thing I've been
trying lately is to define the module hierarchy at the top
of the file, then qualify the class name so that nesting
is avoided. Like,

module CILA end
module CILA::SVC end

class CILA::SVC::Hub

  # ........ methods here as usual ........

end

class CILA::SVC::HubClientConn

  # ........ methods here as usual ........

end

So far I'm pretty happy with this approach.

Regards,

Bill

···

From: "Tony Arcieri" <tony@medioh.com>

Tony Arcieri wrote:

You are proposing a Rube Goldberg contraption whereby the scanner would
require some degree of grammatical awareness. This is a complex solution.
The minor flaw is that you don't like "end" statements. Is the minor flaw
of having repeated end statements really worth the level of complexity the
solution would require?

Complexity never goes away (I imagine there is some Conservation of Complexity defined someplace), so you have to decide where you want it.

If you push it down into the parser, you may gain something at the code-writing level, but odds are that when a new feature is proposed, or a bug discovered, adding or fixing things will be much, much harder.

A magic whitespace addition could be a Pyrrhic victory.

···

--
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

To be fair, if you looked at ruby's lexer and parser you'd know that we're already down a rabbit hole. Your statement completely ignores the complexity already inherent in the language.

Don't chalk me up as a proponent of this idea as I've written more than my fair share of python and hate the language (and especially the libraries).

···

On May 20, 2009, at 15:28 , Tony Arcieri wrote:

And I guess I just can't shut up about this: your solution would require a
scanner with some degree of grammatical awareness for every expression which
contains an indent block. When you start talking about a solution like
that, I really think you're going down a rabbit hole.

It doesn't really. Or at least only simplistically; by re-writing the
input code, attempting to place 'end' in the proper place based on
indentation. It breaks silently if you happen to misplace your
whitespace incorrectly, placing an 'end' in the wrong place.

···

On Fri, 2009-05-22 at 14:33 +0900, Tony Arcieri wrote:

Well great! I'm not really sure how that script works, but cool.
However,
what you have there is a marked departure from how Python actually
works.
But if you're happy with it, great. Go for it and see how popular you
can
make it.

n = 2 * -[1,2,3,4,5].inject(0) do |a, b|:
  c = a * 2
  a * c + b
result = 8 * -if n > 0:
  -n += 10000
else:
  n + 500000
puts result

Once again, worked perfectly on its first time through the
preprocessor.

It would appear that "end's" actually help with readability in a few
cases :slight_smile:

I'm glad to see you have some code. Now put it on github, make a gem of
it, and advocate it :slight_smile:

Here are a few other thoughts on it. (Keeping things friendly and
civil...)

1) maybe it could become more of a generic preprocessor? (i.e. you give
it arbitrary rules, like "see this, change it to this")?

2) maybe it could be even smarter about its extrapolation, i.e. allow

4.times {|n|:
puts n

[accomodate for braces--one fewer keystroke!]

or even

4.times |n|:
puts n

[put in its own do's and end's if not there]

or who even needs :'s?

4.times |n|
puts n

[whenever it sees an ending | without preceding do or {, count it as an
indent based block]

That would be interesting, and much more whitespacey, though slightly
less ruby-y .

To clarify Tony's point from above, you can't really do case statements
using only indentation and :'s. Other constructs would work though
(except for the ambiguities mentioned earlier might prevent anythin but
a preprocessor being able to, for ruby's case).

-=r

···

--
Posted via http://www.ruby-forum.com/\.