Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

There might be other reasons that are more important to you. e.g. if you've been using Modula-3, its syntax might be a reason to switch to Python, but in my mind, things like the iterator protocol and NumPy are better reasons. (Also, you've been asleep under a rock for fifteen years.)

In my case, I've been surprised at how much less syntactic overhead Ruby imposes than Python (despite the explicit "end"!), and how much it matters. Compare:

    def mouse_clicked
      x = mouse_x / @cellsize
      y = mouse_y / @cellsize

      if @cells[x][y] == :empty
        set x, y, :wire
      elsif mouse_button == LEFT
        set x, y, :empty
      else
        set x, y, :electron_head
      end
    end

    def mouse_clicked(self):
        x = processing.mouse_x() / self.cellsize
        y = processing.mouse_y() / self.cellsize

        if self.cells[x][y] is Empty:
            self.set(x, y, Wire)
        elif processing.mouse_button() == processing.LEFT:
            self.set(x, y, Empty)
        else:
            self.set(x, y, ElectronHead)
(Here ElectronHead and the like are assumed to be top-level empty classes in the module; the Ruby equivalent is a Lisp-style atom that doesn't need to be declared.)

To me, the Ruby version has a much higher signal-to-noise ratio.

Another thing: JRuby seems to be a lot more mainstream among Rubyists than Jython is among Pythonistas, which I think means it's a bit more solid.



Hi there...experienced ruby guy here who is doing python work at the moment. The nice thing I find about python, even in its verbosity relative to ruby, is that I always for the most part know what it's doing. With ruby, the impulse to overload operators and create dsls mean that I have to learn a new language (potentially) with every new project or library I choose to use. It's not immediately clear what code is really doing. With python, it usually is. Just my (quick) two cents.


Yes, I think the verbosity I'm complaining about above is the fruit of three of the principles of the Zen of Python:

Explicit is better than implicit.

Errors should never pass silently.

Namespaces are one honking great idea -- let's do more of those!

Maybe after I spend more time with Ruby, I'll see the dark side of DSLs, but so far I'm loving it :)


You probably will, if you're anything like me. However, at the end of the day, I find the "require" statement in ruby to be the truly most irritating feature. You simply don't get much control over what is slurped into your code. Python's import statement is so refreshing in comparison. I feel more in control in python, and I say this as someone who really likes ruby.


I don't understand this code. In the Ruby example, mouse_x() appears to be a method on the current object, in the Python one it's a method on some other object (a module named "processing"?). Since the code examples are the same, I'd say whichever language I'm wrong about is the less clear one.


Yes. I don't have a Python version of Processing, so I thought about how the Processing API would best be exposed in Python — and it would probably be by calling functions exported by a module named "processing", rather than by inheriting your class from processing.App. So you're right about both languages.


You could use:

  from processing import ...


I don't understand why you think the Ruby code has better SNR. The python code is two lines shorter. In python, you have to type the (), but that's not really "noise". I do hat the : at the end of python control statements. I always forget them, and it seems like the parser could do it's job just as well without them.


> I don't understand why you think the Ruby code has better SNR.

Because they say the same thing, but the Python code is much longer, if you count tokens instead of lines. It has about 28-30 more tokens than the Ruby code does, about three per line. Those tokens don't convey any information. They're just redundancy, i.e. noise. And there are a lot of them; these redundant tokens are something like a third of the code.

Now, redundancy can be useful (see e.g. http://www.paulgraham.com/redund.html) but it is costly, so you should keep it to the minimum that achieves what you want the redundancy to achieve.


Haskell removes not only the () that Ruby does, but also the commas. It also uses a simple precedence trick to remove almost all () in general (not just for functional calls).

I wanted to translate the code to Haskell directly, but the idioms don't map that well (self.cellSize would becomes something else entirely, and the comparison would not be fair).


I'm curious what it would look like.


I'm curious if you would still think of token count as a useful metric if the Ruby version turns out to be twice the size of the equivalent code in J or K due to "redundant" "noise" tokens. Programs should be written for people to read, and only incidentally for machines to execute.


I do think it's a useful metric, and I am often tempted by the low token count of Forth and APL dialects like J and K, but much of the APL reduction in code size comes not from token-count reduction, but from token-length reduction, which is much less praiseworthy in my book.

Here's Life in Dyalog APL, from http://dfns.dyalog.com/c_life.htm and http://news.ycombinator.com/item?id=1041500:

    life←{                                  ⍝ John Conway's "Game of Life".
      ↑1 ⍵∨.^3 4=+/,¯1 0 1∘.⊖¯1 0 1∘.⌽⊂⍵  ⍝ Expression for next generation.
    }
Unfortunately, my APL is pretty rusty, and Dyalog has added some major extensions to the language, so I'm not quite sure what that means, in particular the juxtaposition of 1 and ⍵ toward the beginning. I think it parses as

        vertically_rolled = outerproduct(⌽, [-1, 0, 1], ⊂(⍵))
        horizontally_rolled = outerproduct(⊖, [-1, 0, 1], vertically_rolled)
        neighbor_counts = sum(ravel(horizontally_rolled))
        ↑(⍵(1, innerproduct(and, or, ⍵,
                            [3, 4] == neighbor_counts)))
but I'm not even sure of that.

The Life rule (which is pretty optimal for being expressed in APL, given its array orientation) is slightly simpler than the Wireworld rule:

      def run_wireworld_rule
        old_cells = @cells
        @cells = fresh_cells

        each_coord do |x,y|
          # This could be optimized somewhat by only recalculating
          # the neighbors of dirty cells.
          case old_cells[x][y]
          when :electron_head
            set x, y, :electron_tail
          when :electron_tail
            set x, y, :wire
          when :empty
            # do nothing; fresh_cells are all :empty
          when :wire
            case electron_head_count old_cells, x, y
            when 1, 2
              set x, y, :electron_head
            else
              # Don’t call `set` in this case so as not to mark 
              # the cell dirty for redrawing.
              @cells[x][y] = :wire
            end
          end
        end
      end

      # This could perhaps be optimized somewhat with a sum table.
      def electron_head_count(cells, base_x, base_y)
        count = 0
        ([0, base_x-1].max..[base_x+1, @nx-1].min).each do |x|
          ([0, base_y-1].max..[base_y+1, @ny-1].min).each do |y|
            count += 1 if cells[x][y] == :electron_head
          end
        end
        return count
      end

      def each_coord
        (0..@nx-1).each do |x|
          (0..@ny-1).each do |y|
            yield x, y
          end
        end
      end      
The APL people claim that their programs are more readable because they're shorter, but I'm not sure how much I believe their claim. It's certainly true that other kinds of mathematical notation benefit enormously from brevity and consistency that permits mechanical manipulation, and I don't see why algorithms should be different, but empirically I have a lot less trouble with Ruby or Python (or even C) than with plain-English descriptions of mathematical equations.


    from processing import LEFT, mouse_x, mouse_y, mouse_button
Now you don't have to prefix things with `processing` just like you don't in your Ruby version.


If the program used them in several places, that would be an improvement, but it doesn't. It's maybe a little bit atypical, but nearly all of the things it calls from Processing, it calls in exactly one site: the above, plus color_mode, no_stroke, smooth, background, and rect.

It does call fill from four call sites, but that was because there's no reasonable way to return a list of four arguments from a method in Ruby, as far as I can tell.


> way to return a list of four arguments from a method

p, q, r, s = * method_that_returns_array_of_4_things

* aka "splat operator"


That's great, thanks! And that works for method calls too. So now I can replace set_color_from @cells[x][y] with fill *color_from(@cells[x][y]), reducing the number of side-effecting methods by one, and now I'm calling every Processing function in at most one place.




Consider applying for YC's Summer 2026 batch! Applications are open till May 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: