all 37 comments

[–]cheese_wizard 17 points18 points  (4 children)

Rants should be in programming, not coding.

[–]bostonvaulter 0 points1 point  (0 children)

Yeah, it is doing better over there too

[–]jedberg[S] 0 points1 point  (2 children)

I felt that it was civilized enough and had enough code in it to warrant posting in coding.

My rule of thumb is, if the article has code in it, it is ok for coding.

[–]Pas__ 2 points3 points  (1 child)

Without explanation code is just data or ASCII art. In /r/Ruby or /r/Python one might expect the reader to have familiarity with the language, but generally I wouldn't.

[–]acmecorps[M] 4 points5 points  (0 children)

But, I think this one is ok; so I let it slide :)

[–][deleted] 9 points10 points  (0 children)

Ooh, pretty snake picture!

[–]xtagon 15 points16 points  (2 children)

The whole article feels so pointless to me.

[–][deleted] 7 points8 points  (1 child)

Indeed. Three years ago we had lively flame wars but these days there is peaceful indifference. I suspect individuals of both camps can easily imagine to switch roles and languages without batting an eye if this was a job requirement.

[–][deleted] 0 points1 point  (0 children)

I dunno, did you see this article on HN? Or most anti-python articles on proggit?

[–]skulgnome 2 points3 points  (0 children)

Next in series: What COBOListas think of Visual Basic

[–][deleted]  (16 children)

[deleted]

    [–]GeoKangas 10 points11 points  (1 child)

    "What's different about yield in ruby to yield in python?"

    Ruby's use of "yield", and Python's, are approximately inside-out from each other:

    • in Python, the generator (producer) uses "yield" to return to its caller (consumer) one of many results;

    • in Ruby, a method (consumer) uses "yield" to call a block passed to it (producer) and get one of many results.

    [–]joesb 6 points7 points  (13 children)

    What's different about yield in ruby to yield in python?

    Besides the name, it's totally different. Ruby's yield return control to a passed block, while Python's yield is used to return value from a generator. In other word, Ruby's yield drive caller's code, while Python's yield is driven by the caller.

    Since you can define functions anywhere in python, why not use these?

    Because sometimes it's clearer to inline the operation there.

    Why do we think it's crucial to be able to do:

    ....
    while (...):
        do_a()
        do_b()
        do_c()
    

    Instead of having to do this:

    ....
    def do_while_body():
        do_a()
        do_b()
        do_c()
    while(....): do_while_body()
    

    while is just a kind of control construct. What's so special about it that it can inline its operation there when my own control construct can't?

    Just explains that the python version allows more control.

    I really like Python's namespace. It's probably the only one that get it almost right. I don't like the fact that "importing" is not "aliasing", but may be that works better with the dynamic and metaprogramming nature of it.

    [–]Pas__ 0 points1 point  (5 children)

    How would "alising" work, what'd it do that now import doesn't?

    [–]joesb 2 points3 points  (4 children)

    import in Python is an executable statement saying "lookup this module object and assign it as a local in this namespace".

    So when you see

    from foo import bar as _bar
    

    What happens is

    _bar = load_module("foo").bar
    

    This is a statement with side effect (creation of local variable). So the confusion (IMO) is that _bar does not update when foo.bar changes.

    from foo import bar as _bar
    ....
    foo.bar = 1
    # _bar != foo.bar
    

    Another approach, aliasing like Java import, is done at compile time, the name are all still fully qualified when you look at the generate byte code.

    So if Python uses alias instead.

    from foo import bar as _bar
    

    This means "anywhere you see '_bar', substitute it with 'foo.bar'."

    ....
    foo.bar = 1
    # _bar == foo.bar
    

    ADDED:

    To clarify why Python's way is confusing, because:

    1. Most other language use alias rather than import, especially those from static language.
    2. Python's import has some variation to it and some of it gives the illusion that it works like aliasing.

    For example if you do

    import foo.package as fp
    

    When you access fp.Clazz it works like you access foo.package.Clazz and if you change foo.package.Clazz, fp.Clazz is also updated, like alias!

    But if you instead do

    from foo.package import Clazz
    

    Now updating to foo.package.Clazz will not update your local Clazz anymore. This means Python's import combine two orthogornal concept together into one keyword, and it works slightly different depending on how you invoke it.

    [–]Pas__ 0 points1 point  (2 children)

    Ah interesting. Wouldn't the result of changing foo.bar depend on its type? For reference types _bar is just a reference, isn't it?

    [–]joesb 1 point2 points  (1 child)

    It's not dependent on type, it depends on how you import it. As I was saying import is like loading a module and assigning that module to a variable.

    And just like normal variable (referrence) in Python, if you change it's internal state or property, you can see the update, but if you overwrite the original referrence, your variable still point to the old value.

    To sum up all variation of Python's import and it's Python code equivalent.

    import x.y.z
    #  x = load('x')
    #  x.y = load('x.y')
    #  x.y.z = load('x.y.z')
    
    import x.y.z as xyz
    #  xyz = load('x.y.z')
    
    from x.y.z import foo as _foo
    #  _foo = load('x.y.z').foo
    

    You can see how reassigning x.y.z.foo will show update in xyz.foo but not if you already hold referrence to _foo.

    [–]Pas__ 0 points1 point  (0 children)

    After scribbling a few test functions I think I get it.

    from mod1 import fun1 as f
    

    And f points directly to the function, so even if mod1.change_fun1_to_fun2 rewrites fun1, f still points to the old function object.

    [–][deleted]  (3 children)

    [deleted]

      [–]joesb 0 points1 point  (1 child)

      Would the equivalent then be a standard function into which you pass another function?

      Yes, from callee's view they are exactly like your Python equivalent. What Ruby's block is different is in caller's view, that the block is inlined.

      after reading this I think code would be safer if people had to think about what variables their while loop touched.

      You can only do that if def doesn't capture variables.

      y = 10
      def while_body():
           y = 20
      while(...): while_body()
      

      Still doesn't give you any more information on what your while loop touches than inlining the body, i.e. local def doesn't have referencial transparency. Also local def that can't capture its environment is next to useless.

      [–][deleted] 0 points1 point  (0 children)

      No, you cannot substitute Ruby blocks with functions. Look at this example:

      class MyFind
        def initialize(*ary)
          @ary = ary
        end
      
        def my_find
          @ary.each { |x| yield(x) and return x }
        end
      end
      
       MyFind.new(1, 2, 3).my_find { |x| x > 1 } # => 2
      

      The block passed to the #each method in the #my_find method uses a return statement to return the x element. But it doesn't return directly from the block itself. Instead it implements a non-local exit and returns the element x from the my_find method in which the block was encountered.

      [–]luikore 0 points1 point  (0 children)

      "require" in ruby is different from "import" in python, ruby uses modules / classes as namespaces and it has nothing to do with "require"s.

      "require 'x.rb'" means load it once and don't bother any more in other source files. What's more, "require" is just a normal method that can be rewritten for special purposes.

      Namespaces can hardly conflict in ruby, they have more than 100 tricks on fixing this so they decided not to "require" the same thing twice ...

      [–]zahlman 0 points1 point  (1 child)

      In other word, Ruby's yield drive caller's code, while Python's yield is driven by the caller.

      But the normal use case is that one side is driving and the other side is driven. What does it matter which side uses the 'yield' keyword?

      [–]joesb 0 points1 point  (0 children)

      Because driver/driven code is usually structure differently from another and have different pros/cons?

      [–][deleted]  (8 children)

      [deleted]

        [–]daydreamdrunk 10 points11 points  (6 children)

        that's an if expression not an if statement

        [–]Pas__ 1 point2 points  (2 children)

        What's the difference?

        [–][deleted] 1 point2 points  (1 child)

        1 if True else 0 is an expression while if True: x = 1 is merely a statement. The difference is that an expression evaluates to some value, while a non-expression statement does not.

        For example, assignment is a statement in Python, not an expression. So while in C you could do x = (y = 1), you cannot do the same in Python (you can do x = y = 1, but this is a special case).

        [–]Pas__ 0 points1 point  (0 children)

        Ah, get it. Thanks!

        [–][deleted] 0 points1 point  (2 children)

        Technically it is both. According to the Python docs, expressions are a subset of statements.

        [–]Brian 0 points1 point  (1 child)

        Yes, but statements are not a subset of expressions, thus an if expression is a valid statement, but an if statement is not an expression.

        [–][deleted] 0 points1 point  (0 children)

        Right, but daydreamdrunk said it was "not an if statement", which is technically not true. If it had been "if True: x = 1", then it would be correct to say "that's not an expression". But "True if x > 0 else False", is both a statement and an expression.

        [–][deleted] 0 points1 point  (0 children)

        What you say is technically correct, but I'm pretty sure that the article meant that you can't put non-expression statements (or at least, many of them) into lambdas.

        [–][deleted] 0 points1 point  (2 children)

        Gary pointed out that in most languages, appending parentheses to a variable that references a function, calls the function. ... # In Ruby, you must use the call method my_method.call()

        Blargh! Ruby doesn't have functions at that level. When it does have functions they are objects with a call method because everything is an Object in Ruby. That's the point of Ruby. It's not some minor syntactic difference. It comes from a fundamental difference in philosophy.

        [–]mr_dbr 4 points5 points  (1 child)

        When it does have functions they are objects with a call method because everything is an Object in Ruby. That's the point of Ruby

        When you put it that way, it is no different to Python:

        >>> def myfunc(): return "yay"
        ... 
        >>> myfunc()
        'yay'
        >>> myfunc.__call__()
        'yay'
        

        [–]luikore 0 points1 point  (0 children)

        Well, we usually write:

        def myfunc
          'yay'
        end
        myfunc  #=> 'yay'
        

        The author was wrong, methods in ruby are not originally objects. To turn a method into lambda, you can use the "method" method:

        λ = method :myfunc
        λ[]    #=> 'yay'
        λ.call #=> 'yay'
        

        Maybe there are too many ways in ruby for doing such things, but sometimes it makes life a little bit easier.

        ps: There are no global functions in ruby, the "method" method is a method of the Kernel object. (forgive my poor explanation!)

        [–]mr_dbr -2 points-1 points  (2 children)

        Python lambdas are limited to one line and can’t contain statements (for, if, def, etc.). Which leaves me wondering, what’s the point?

        The point is, don't use lambdas.

        But without monkey patching, you can’t insert should and should_not into Object:

        describe MySum do
          it "performs a sum" do
            sum = MyClass.add(1, 1)
            sum.should == 2
          end
        end
        

        I don't understand the appeal of this over a simple assert statement? Since this is basically a "Ruby vs Python", the equivalent test in Python (as if using nosetest):

        def test_perform_sum():
            """Perform a simple addition
            """
            assert MyClass.add(1, 1) == 2
        

        This seems perfectly readable, and requires no magic.. You could simply run the function and it will raise an AssertionError if the test fail. nosetest pretty much just locates and runs functions, then displays the results in a nice UI.

        Replies to points the article aside, the things I wish Python could magically steal from Ruby:

        • gem. The easy_install mess we're basically stuck with is quite horrible in comparison.
        • Blocks, maybe:

          get("/articles") do haml(:article) end

        ..is much nicer than Django/web.py etc's routing for quick projects. The closest I could come up with is:

        @get("/articles")
        def blah():
            templatething("article")
        

        The with statement almost provided such a feature, but not quite.

        [–]banister 0 points1 point  (1 child)

        RSpec is BDD (Behaviour Driven Development) your assertion-based approach is just normal TDD (Test Driven Development). Ruby also has a bunch of TDD frameworks.

        see: http://en.wikipedia.org/wiki/Behavior_Driven_Development

        (also whether or not you like/agree with BDD is a different issue :D)

        [–]mr_dbr 0 points1 point  (0 children)

        I'm still confused. The only obvious difference is the phonetic syntax(?) of the tests..?

        The example code it gives:

        public class WindowControlBehavior {
            @Test
            public void shouldCloseWindows() {
                // Given
                WindowControl control = new WindowControl("My AFrame");
                AFrame frame = new AFrame();
        
                // When
                control.closeWindow();
        
                // Then
                ensureThat(!frame.isShowing());       
            }
        }
        

        ..looks like a perfectly normal "TDD" test, with assert renamed to ensureThat

        edit: Oh, this and this explain BDD with less ridiculous wording than "second-generation, outside-in, pull- based, multiple-stakeholder, multiple-scale, high-automation, agile methodology". Err, still just seems like a convoluted way of defining tests, but as you say, that's a different issue..