[HN Gopher] Ruby vs. Python comes down to the for loop
       ___________________________________________________________________
        
       Ruby vs. Python comes down to the for loop
        
       Author : softwaredoug
       Score  : 310 points
       Date   : 2021-11-12 14:55 UTC (8 hours ago)
        
 (HTM) web link (softwaredoug.com)
 (TXT) w3m dump (softwaredoug.com)
        
       | stefanchrobot wrote:
       | Why not both? I want the language to provide an
       | iterable/enumerable interface with the usual generic workhorses
       | (map, reduce, etc.) and allow me to implement that interface as a
       | free function for any type. If I'm authoring a type, I'll add
       | specialized functions as needed.
        
         | aardvark179 wrote:
         | Ruby manages to do this. The default implementations of all the
         | methods you want are on the enumerable module, so if you
         | implement the "each" method you get everything, including the
         | ability to get an enumerator object that you can pass round and
         | use for anything else you want.
        
       | epage wrote:
       | For me, its the explicitness in Python that is killer. I can see
       | a name and match that to an import and match that to a package.
       | Grabbing a function pointer works exactly as I would expect.
       | 
       | Stealing from Krister Stendahl's laws for religious discourse,
       | Ruby's composable iteration is one area I have holy envy,
       | particularly after I got used to it in Rust. I've used Python's
       | generators many times just to have to switch to an explicit for
       | loop. Things are generally better with a composable iteration
       | model but occasionally I find myself switching to explicit loops
       | in Rust.
       | 
       | Unlike Ruby, Rust does pull-iteration like Python, though there
       | are experiments with Ruby-style push-iteration [0] [1].
       | 
       | [0] https://github.com/AndWass/pushgen
       | 
       | [1] https://epage.github.io/blog/2021/07/pushgen-experiment/
        
         | efxhoy wrote:
         | > I can see a name and match that to an import and match that
         | to a package.
         | 
         | I miss this so much. I'm a python dev now working on a big
         | Rails monolith. Where does all this stuff come from?
        
           | joelbluminator wrote:
           | Are you using a good IDE? With Rubymine (which indexes all of
           | your gems) this is a much less big a deal.
        
           | rubyist5eva wrote:
           | I use Rubymine and can cmd+click on anything and it shows me
           | exactly where it came from. Been developing in rails for a
           | decade and the auto loading has bitten me at most 5 times.
        
           | handrous wrote:
           | Onboarding to all but the most perfectly-written-and-
           | maintained Rails codebase is pure hell, for exactly that
           | reason. I've done lots of Rails in the past, but sworn it off
           | after enough such experiences.
           | 
           | [EDIT] Which sucks, because I really like Ruby.
        
           | Lio wrote:
           | If you ever wonder where something comes from and you can't
           | get your editor/IDE to tell you, you can always try asking
           | the runtime.
           | 
           | Drop a debugger statement in above the code in question, then
           | at the prompt turn the method into a Method object using
           | something like:                 bar_method = foo.method(:bar)
           | 
           | and then                 bar_method.source_location
           | 
           | Once you have a Method object you can pass it around as a
           | stand alone function, rebind it or call too.
           | 
           | #source_location only works for methods defined in ruby
           | itself i.e. not C or Java but it's still a handy tool on big
           | ruby projects.
           | 
           | Most of the time I just use ctags to navigate new codebases
           | but we also have things like Solargraph via LSP too.
        
             | mypalmike wrote:
             | > you can always try asking the runtime
             | 
             | 90% of the time, I'm trying to read code in a very local
             | context and understand it, which involves being able to
             | trace an identifier to its source. I don't want to run it.
             | Maybe I can't run it (it's a rarely executed path, the
             | runtime env is complex, someone emailed me a copy of a
             | source file, etc.).
             | 
             | Language developers, be like Python. Be like Java. Be
             | boring and explicit.
        
             | nitrogen wrote:
             | _Drop a debugger statement in above the code in question_
             | 
             | If you're using Pry as your REPL, you can use Pry's _ls_
             | and _show-source_ commands to get more of the info you want
             | at once, with less typing. It 's basically calling the Ruby
             | introspection methods for you.                   ls foo
             | show-source -d foo.bar
        
             | friedman23 wrote:
             | Having to use a debugger to determine the types of
             | variables at runtime is just so terrible and backwards.
             | I've had to debug massive python projects that have used
             | monkey patching in the framework and it was terrible. This
             | thread is actually causing me pain and making me relive
             | horrible bugs i've had to fix. Reading about auto loading
             | and having to use a debugger to inspect types is just so
             | fucking bad.
        
         | qudat wrote:
         | > I can see a name and match that to an import and match that
         | to a package. Grabbing a function pointer works exactly as I
         | would expect.
         | 
         | Autoloading is one of the worst features ever introduced into a
         | language/framework.
         | 
         | https://erock.io/2021/11/01/on-autoloading.html
        
           | byroot wrote:
           | Again, your complaint, while valid, has nothing to do with
           | autoloading, but with namespacing:
           | https://news.ycombinator.com/item?id=29117535
        
           | friedman23 wrote:
           | Ah yes, now I remember why I stopped learning ruby and was
           | generally disgusted with the language. Along with the
           | standard library having numerous ways to do the same thing
           | and the general use of monkey patching (and the ruby users
           | actually thinking monkey patching is good)
        
           | pas wrote:
           | I have no problem with PHP autolading, or TS dependency
           | injection (as seen in Angular, NestJS, TSed), or even in
           | Scala (Play! framework via Guice). Because it's not magic
           | (since you need to import the types you know what you are
           | getting, for PHP there's the autoload.php generated by
           | Composer, for TS everything is in the node_modules and in the
           | lockfile, and for both Scala and TS the compiler checks
           | things too. Of course in TS there are sometimes problems for
           | non-native [ie. JS + .d.ts] libs, when the typing becomes
           | outdated).
           | 
           | But in Python the amount of magic shit one had to do to get
           | things loaded in a sane manner was always somehow a serious
           | burden.
           | 
           | .. but in Ruby (Rails). Well, yeah, all you have is a
           | lockfile, and nothing else. No imports, no typing. That gets
           | crazy really fast. :)
        
             | saila wrote:
             | You can use DI in Python too--it's an architectural pattern
             | that can be used in any project regardless of language.
             | 
             | I've never seen or used autoloading in Python. What would
             | the use case for that be? I guess it could be "convenient"
             | to avoid imports? That seems like a bad idea to me and not
             | very "Pythonic."
             | 
             | I'm not sure if this is relevant to your comment, but I've
             | seen people abusing _sys.path_ in Python projects instead
             | of setting up a proper package and installing it.
             | 
             | Modern Python package/dependency managers like Poetry make
             | this nicer/easier than the old school _setup()_ approach
             | and they also create lock files.
        
             | shakna wrote:
             | > But in Python the amount of magic shit one had to do to
             | get things loaded in a sane manner was always somehow a
             | serious burden.
             | 
             | Controlling how Python imports things, seems dead easy with
             | importlib [0] (introduced in Python 3.1). You can control
             | the namespaces, the loader, the paths, generate code on the
             | fly, etc.
             | 
             | If you wanted to, say, duplicate lockfile imports using a
             | requirements.txt file, then that's probably a teeny tiny
             | ten line thing or something like that.
             | 
             | Python is quite flexible if you want to rewrite how the
             | language is doing something.
             | 
             | [0] https://docs.python.org/3/library/importlib.html
        
         | softwaredoug wrote:
         | Author here - I agree on explicitness, it can be a bit
         | maddening to trace where a symbol comes from in Ruby. Though a
         | good IDE helps.
        
           | lamontcg wrote:
           | https://tenderlovemaking.com/2016/02/05/i-am-a-puts-
           | debugger...
        
             | nitrogen wrote:
             | _> I think it's important to say that when you're debugging
             | anything goes. I mean anything. Global variables,
             | redefining methods, adding conditionals, manipulating the
             | load path, monkey patching, printing the call stack,
             | anything._
             | 
             | This is the thing that keeps me coming back to Ruby. The
             | core philosophies of the people who influence Ruby just
             | align so well with my own.
             | 
             | [Back in 2008ish I had a choice to learn Ruby or Python,
             | after working with languages including C and Java. I
             | started with Python, but switched to Ruby because Python
             | launched slower and had no native Integer type (important
             | for old ARM CPUs of the era with no FPU), but it's Ruby's
             | philosophy and potential for poetic (but readable!) flow
             | that make it stick now over a decade later]
        
         | Groxx wrote:
         | Well.... except when the package doesn't match the import path.
         | Then you can get stuck in a merry search for "what the heck is
         | X and why is it not in our lockfile".
        
         | qsort wrote:
         | I agree.
         | 
         | I'm not the biggest Python fan myself and would not recommend
         | it for large projects, but I use it a lot both at work and
         | personally for scripting/small stuff. It feels dumb, but I
         | _like_ dumb for those use cases.
        
         | rgoulter wrote:
         | > I can see a name and match that to an import and match that
         | to a package.
         | 
         | I like this too, but... I don't find dynamic languages to be
         | very explicit.
         | 
         | Given that Python is a dynamic language, I only going to know
         | what I can do with (or what is true about) the arguments to
         | some function if I have worked with the codebase previously (or
         | have sufficiently good documentation, and unit test coverage).
        
           | horsawlarway wrote:
           | At least with an explicit import you have _something_ to
           | search for.
           | 
           | I have often spent several minutes trying to find a
           | definition for a ruby method/modules/class only to finally
           | have to run it and dump the file location manually with
           | `source_location` to find out it's really a 3rd party gem
           | added to the project.
           | 
           | Admittedly, Rails makes this much worse.
        
             | rgoulter wrote:
             | Let alone other 'magical' techniques Ruby affords, e.g. you
             | can re-open a class to monkey-patch more methods in; or
             | even define method_missing so that the methods an object
             | has are dynamic. Some very neat stuff.
        
       | errcorrectcode wrote:
       | I use Crystal more these days because it looks like Ruby but it's
       | typed and statically compiled.
        
         | friedman23 wrote:
         | Crystal does look interesting, it also supports parallelism
         | with green threads right? The thing that always bothered me
         | about ruby is that there are multiple ways to do the same thing
         | in the standard library.
        
           | yxhuvud wrote:
           | Yes, it has green threads.
           | 
           | Crystal don't have the same tradition of having aliases of
           | common methods as Ruby does, no.
        
         | derwiki wrote:
         | More for professional or hobby projects?
        
           | errcorrectcode wrote:
           | Hobby mostly. Not very often. Use Rust mostly.
        
       | [deleted]
        
       | dehrmann wrote:
       | Interestingly, Java has _both_ styles. You can do
       | for (var i : list) {...}
       | 
       | or                   list.forEach(i -> ...);
       | 
       | Except for cases with local variables, I have absolutely no idea
       | which I should prefer. Kotlin makes a cute case for the
       | functional style with it's lambda shorthand:
       | list.forEach {             ...         }
       | 
       | I've found the functional style helpful for times I want almost
       | add a language feature to avoid boilerplate. The functional style
       | was added without special-purpose language changes, but the
       | iterator version required Java to add Collections, Iterators, and
       | eventually fancy for-each syntactic sugar.
        
         | entropicdrifter wrote:
         | The big advantage of the functional style in Java is that you
         | are disallowed at the compiler level from modifying the
         | contents of the collection you're iterating over. That sort of
         | compiler-enforced good practice is definitely for the best.
        
       | dvdhnt wrote:
       | I'll be absolutely subjective here.
       | 
       | Python seems fine, except that I can't get comfortable in a
       | language that uses whitespace as part of its syntax.
       | 
       | I know the typical arguments of "linter, etc" but I believe my
       | ruby and JS code, which includes using a linter and formatter, is
       | easy to reason because brackets allow for better mental
       | encapsulation.
       | 
       | All personal experience, I can't speak to the technical
       | differences.
        
         | tonmoy wrote:
         | Having used both professionally I love Ruby and I think python
         | is an ok language. However, if there is one thing I do like
         | about Python it is the white space syntax. It forces all coders
         | including the not-so-technical people to write scripts using
         | proper indentation and it makes inheriting Python scripts a
         | little bit easier
        
           | derac wrote:
           | You can have your IDE indent code in the style you prefer
           | automatically.
        
             | tonmoy wrote:
             | I'm looking at diff between two commits right now where
             | someone decided to fix the indentation in a non-Python
             | language. While the new code is very easy to read, the diff
             | is very difficult to analyze.
        
             | indymike wrote:
             | > You can have your IDE indent code in the style you prefer
             | automatically.
             | 
             | If you look at significant whitespace through the lens of
             | developer preference, then sure, it sucks. Where it shines
             | is ensuring code is readable by people other than the guy
             | who likes 320 character long lines, and likes aligning
             | everything to the = sign. The only language I know that is
             | as easy to read as Python is Go, and it does so by forcing
             | developers to use go fmt.
        
               | mixmastamyk wrote:
               | Indentation instead of brackets in Python was a
               | masterstroke in my opinion. Despite complaints from folks
               | that don't use it much, and therefore run into an issue
               | here and there due to lack of familiarity.
               | 
               | As a regular user the tradeoff pays great dividends every
               | single day in readability for years on end. I would
               | remove more punctuation if I could... every single new
               | feature to Python seems to include colons lately. :-/
               | 
               | No, I don't miss brackets everywhere one bit.
        
         | drcongo wrote:
         | I think once you get more used to it you'll find the complete
         | opposite. All those brackets are extra things your brain needs
         | to process when reading the code, strip them away and it
         | becomes just like reading written language. Ruby to my eyes,
         | even when well formatted, looks as ugly as PHP - a mess of
         | unnecessary indications that destroy the readability of the
         | code (for me).
        
           | arcticfox wrote:
           | As a Python->Ruby convert like the parent here, I'm curious,
           | which indications do you find unnecessary? The only one I can
           | really think of is `end`, which, sure. But the implicit
           | return is also much cleaner after I got used to it, so I
           | think that's a wash.
        
             | drcongo wrote:
             | I've barely written any Ruby in my life, so I may well
             | change my mind like you if I did more. I do find it
             | interesting how some languages just look uglier than
             | others, and how subjective that is. I really struggle to
             | learn languages where my initial reaction to how it looks
             | is bad.
        
           | dragonwriter wrote:
           | > All those brackets are extra things your brain needs to
           | process when reading the code, strip them away and it becomes
           | just like reading written language
           | 
           | No, it doesn't, because written language explicitly marks
           | ends, and sometimes also begginings, of structural units
           | (programming gets brackets from natural language), it doesn't
           | just use white space (until you get to units bigger than
           | sentences.)
        
             | Thrymr wrote:
             | > No, it doesn't, because written language explicitly marks
             | ends, and sometimes also begginings, of structural units
             | (programming gets brackets from natural language), it
             | doesn't just use white space (until you get to units bigger
             | than sentences.)
             | 
             | Written language does of course use white space to separate
             | words (this was not always the case, see for example
             | classical Latin).
             | 
             | And I think it's fair to say that a line of Python is
             | roughly equivalent to a sentence, and whitespace
             | indentation is only used for syntax at that level.
             | 
             | The analogy breaks down a bit at higher levels, as Python
             | blocks can recurse, but in writing we have structures like
             | chapters, books, etc.
        
         | kevinmgranger wrote:
         | I really wish there were languages with tooling to expose the
         | source as either whitespace-using or bracket-using. It would
         | solve so many conflicts.
         | 
         | Let's have it handle tabs or spaces while we're at it. The
         | answer could always be "whatever you prefer".
         | 
         | My kingdom for better tooling!
        
           | chaxor wrote:
           | I also love the cleanliness of Python compared to bracketed
           | languages. When I was younger and learning the language it
           | seemed to be beneficial in forcing more structure and
           | readability in the code (no ridiculous obfuscation by single
           | line programs).
           | 
           | There was an article the other day showing how to take latex
           | math notes in neovim the other day. They showed a plugin that
           | displays a render of text on lines that the cursor is not on.
           | 
           | Perhaps there is a way to set that up for languages with
           | brackets? Just show the brackets relevant to that line (and
           | the closing ones perhaps)
        
           | intrepidhero wrote:
           | I've been thinking a lot about that too. I wonder if we could
           | have tools where the primary artifact is some kind of AST (a
           | concrete syntax tree maybe?) that the tools translate into
           | the language of the programmers choice. Seems sort of like
           | the next step beyond bytecode. But I'm just speculating and
           | daydreaming at this point.
        
           | dagw wrote:
           | _Let 's have it handle tabs or spaces while we're at it. The
           | answer could always be "whatever you prefer"._
           | 
           | As long as you don't mix tabs and spaces in the same file
           | python is fine with either.
        
             | kevinmgranger wrote:
             | It's not about what the language is fine with. It's about
             | what the developers are fine with. And they'll never agree,
             | so why not give them a choice with tooling?
        
         | UncleOxidant wrote:
         | I was a Ruby early adopter - early 2001 - because I needed to
         | move from Perl to a new scripting language (OO Perl just seemed
         | like such a hack and there really was something to the Write
         | Once aspect of Perl - I'd come back to a Perl program 6 months
         | after I wrote it and wonder what the heck was going on). I took
         | one day to play with Python. I got burned by the significant
         | whitespace thing right away. Next day I looked at Ruby and it
         | seemed like the perfect fit for what we were doing. Over the
         | next dozen years or so I was a huge Ruby fan. But then around
         | 2014 I got a different job in a Python shop. I thought I was
         | going to have a rough time with the significant whitespace, but
         | I ended up liking it a lot. That's not to say that I don't miss
         | features from Ruby, but I found that I could get along fine
         | with Python's significant whitespace.
        
         | stavros wrote:
         | I'm subjective the other way, why do I need to type brackets
         | when my indentation already carries my intention?
        
           | setpatchaddress wrote:
           | You need to spend a couple of days debugging scripts where
           | someone has fucked up the indentation by copying and pasting
           | with a program messes with leading whitespace. Or where
           | someone has inserted an extra whitespace character in a
           | random line in a long script.
        
             | stavros wrote:
             | I've been a Python developer for twenty years, I'm sure
             | those things are bound to happen at some point but haven't
             | yet.
        
               | horsawlarway wrote:
               | I was a TA for a physics lab that used python (3 months)
               | 
               | This came up nearly 100+ times in those 3 months, just in
               | the classes I TA'd for.
               | 
               | Making invisible characters carry significant meaning
               | _really_ throws non-programmers for a loop at times: It
               | 's _so_ easy, right until they break it and don 't
               | understand how.
        
               | stavros wrote:
               | Isn't it the same when their code won't compile because
               | of an extra bracket?
        
               | horsawlarway wrote:
               | Not really - A missing bracket is immediately a parsing
               | error.
               | 
               | Mis-indented whitespace is _not_.                 if(err)
               | {          console.log('something bad happened')
               | recover()       continue()
               | 
               | Immediately throws an unmatched brackets error.
               | 
               | No such luck for                 if err
               | print("something bad happened")         recover()
               | continue()
               | 
               | now you can spend minutes trying to figure out why
               | continue isn't being called as you expect it to in the
               | success case. It's obvious enough once you open that
               | specific file and see the indentation, but the tooling is
               | no help at all.
        
               | mixmastamyk wrote:
               | You can just as easily make a mistake in bracketing as
               | mentioned, in any number of ways.
               | 
               | If you're blindly running software that has never been
               | looked at, tested, and even run once to verify it works,
               | the main problem is elsewhere.
        
               | horsawlarway wrote:
               | > You can just as easily make a mistake in bracketing
               | 
               | I actually strongly disagree with this. There's a reason
               | basically every style guide under the sun defaults to
               | requiring brackets for things like single-line
               | conditionals (where they're optional in a lot of
               | languages): It removes this class of error entirely.
               | 
               | There are lots of other ways to fuck things up with
               | brackets, so no arguments there.
               | 
               | > If you're blindly running software that has never been
               | looked at, tested, and even run once to verify it works,
               | the main problem is elsewhere.
               | 
               | I don't think this is really relevant. In this context -
               | the goal of the student isn't to produce good python
               | code, it's to complete the lab they're working on with
               | their partners. The single biggest case I see where this
               | happens is as follows:
               | 
               | 1 - Student A writes some code
               | 
               | 2 - Student B writes some code
               | 
               | 3 - Student A wants to use what student B wrote
               | 
               | 4 - Student A copies the code from student B into their
               | file, but the indentation is wrong.
               | 
               | 5 - Student A then starts removing indentation (line by
               | line - which is the real mistake, really) and goes one
               | line too far, or one line too short.
               | 
               | 6 - Student A and B test one of the cases for the
               | integrated code (either the success or the failure) and
               | it works fine (in my example, imagine they test the error
               | case and see it does what they expect)
               | 
               | 7 - Then later they realize the other case is breaking,
               | but they don't know why: usually because it's been half
               | an hour since they pasted that code into their editor,
               | and checking indentation doesn't immediately come to
               | mind.
               | 
               | 8 - I get called over to help
        
               | scarygliders wrote:
               | Isn't the actual error in that Python being the missing
               | ":" after "if err"? ;)
               | 
               | Then start worrying about the bad indentation :)
        
               | horsawlarway wrote:
               | Damn you linter-less HN textbox! :D
        
               | mixmastamyk wrote:
               | If you're honestly having trouble with that, the solution
               | is an editor that shows whitespace. Everything above
               | notepad is capable. I use it with a subtle theme that
               | makes tabs just visible enough to stand out. Indentation
               | guides are helpful as well. Geany is a good choice for
               | beginners, free and x-platform.
               | 
               | Black can also diagnose/fix some issues.
        
           | addicted wrote:
           | A language that uses parentheses does not preclude one from
           | also using indentation to convey code structure. In fact,
           | this is usually standard practice.
           | 
           | Python, however, does indeed preclude one from using
           | additional signifiers of code structure beyond indentation.
           | 
           | And in practice you don't type out parentheses. You put in a
           | single opening parentheses and your editor will add the
           | closing parentheses and add the appropriate indentation (and
           | if desired, newlines) for you. In practice the only
           | difference in typing is typing a single ( character vs a
           | single <tab> character.
           | 
           | All that being said, this is the very definition of bike
           | shedding, so it's really not a factor when picking one
           | language over another.
        
             | stavros wrote:
             | I don't understand what you mean. How are parentheses
             | relevant? Also, personally I hate the parenthesis
             | completion, it gets in the way more than it helps.
             | 
             | By "parentheses" do you mean "brackets"? If so, sure, you
             | can use indentation along with brackets, but why am I now
             | using two things? With Python, all I need to do is
             | backspace every once in a while, to signify the end of
             | indentation, which is very easy to do.
        
               | wizzwizz4 wrote:
               | > _By "parentheses" do you mean "brackets"?_
               | 
               | I choose to believe that this means S-expressions.
        
               | horsawlarway wrote:
               | As a real reply -
               | 
               | I can copy code marked up with parens and brackets from
               | basically any source (no matter how it's indented or how
               | whitespace is handled) and my linter will automatically
               | understand and format it nicely.
               | 
               | In an indentation language - If I copy a chunk of code
               | that's nested more or less deeply than the section I'm
               | currently working on, I have to manually replace
               | whitespace everywhere. Sometimes the linter is smart
               | enough, sometimes it's not.
        
               | wizzwizz4 wrote:
               | If your editor keeps the selection once you paste, you
               | can just hit tab / shift + tab until the indentation's
               | correct. And there's no counting "did I copy the correct
               | number of close brackets".
               | 
               | I actually find Python easier in this regard, even in
               | editors where I have to re-select everything I just
               | pasted.
        
           | jimbokun wrote:
           | Because there are places where indentation signifying
           | intention breaks down.
           | 
           | The classic example being the arbitrary restrictions on the
           | Python lambda syntax.
           | 
           | Ruby is more orthogonal and systematic. Everything is an
           | object, and you call methods on those objects. And sending a
           | block as a method parameter gives you something that very
           | much resembles functional programming (minus the
           | immutability).
           | 
           | There are a few other constructs (it's not quite so dogmatic
           | about object orientation as Smalltalk), but Ruby is very
           | regular and so you can understand how everything works with a
           | small number of syntactic constructs.
        
             | larkost wrote:
             | The lambda thing is not about whitespace indentation. It is
             | a purposeful decision that if you need more than one line
             | to do it, it should have a name.
             | 
             | At first I disliked this, but it does nudge you towards
             | readability, which winds up to be a good thing. And if you
             | look at it, there is very little space difference between
             | an unnamed lambda and an embedded function:
             | def lambda_version():         a = lambda x:           line
             | one           line two # note: this sort-of requires the
             | idea of implicit return, which Python does not have outside
             | of lambda         a()                 def named_version():
             | def lambda_replacement():           line one
             | return line two         lambda_replacement()
        
             | wizzwizz4 wrote:
             | > _The classic example being the arbitrary restrictions on
             | the Python lambda syntax._
             | 
             | That's actually because Guido thinks multi-line expressions
             | are messy to parse, rather than because they _can 't_ be
             | done. Consider:                 f = lambda x:
             | return x + 5
             | 
             | Or, perhaps:                 print(sum(map(lambda n, b:
             | while n:               b.append(n)               n -= 1
             | return b       , range(3), [[], [], []]), []))
             | 
             | You can see why this was never added to the language,
             | though.
        
         | cardanome wrote:
         | Do you have "show whitespace" enabled on your IDE?
         | 
         | I think it really helps being able to see the characters
         | properly. (Also helps avoiding mixing tabs and spaces like some
         | bloody animal.)
         | 
         | While there is some value in whitespace-insensitive syntax, I
         | think using whitespace is a neat and human friendly way to keep
         | things separated and to show structure. We are good at spatial
         | thinking
         | 
         | For example this text is using whitespace between words to tell
         | you where which words starts and ends. It uses paragraphs to
         | further split up certain ideas. (Same with math notation which
         | also uses whitespace)
         | 
         | {[Do; you; think; that; this; is; easier; to; read; ?][Does;
         | it; feel; more; comfortable;?]}
         | 
         | Getting used to new syntax is not as hard as people pretend it
         | is. Never understood the aversion of some people. For Python
         | being whitespace sensitive is a great choice as it helps the
         | pseudocode look of it and is often used as a beginner language.
         | You want beginners to learn how to properly indent code and to
         | suffer if they mix tabs and spaces. (Though maybe now that we
         | have automatic code formatting in many languages, maybe less
         | so.)
        
       | antod wrote:
       | To me, the biggest fundamental difference between Ruby and Python
       | seems to be that:
       | 
       | In Python methods are implemented via callable attributes (it's
       | dicts all the way down)
       | 
       | In Ruby attributes are implemented via methods (it's messages all
       | the way down)
       | 
       | I've been working professionally in Ruby for 5+ years, but non-
       | professionally using/dabbling Python for 15 before that.
       | Personally I still find Python more intuitive though even if I
       | haven't used it for ages - it just fits my brain more
       | automatically and quicker. Ruby is nice overall (ie the smalltalk
       | inspired bits), but the more Perl inspired bits irk me, and parts
       | of the Ruby community can produce some insane library/framework
       | code trying to make interfaces as "elegant" as possible.
        
         | toxik wrote:
         | As an almost exclusive Python programmer, I've often looked at
         | Ruby's block syntax with jealous eyes. It would be so nice. But
         | I wonder if that's maybe the exact thing that I also like about
         | Python: the language is pretty damn simple in its essence. You
         | want to minimize cleverness wherever possible, and that idea is
         | firmly planted in the community. Ruby has this sense that Ruby
         | is almost a meta language, you create cool "DSLs" in it to
         | accomplish your goal. That makes it harder to fit into the
         | brain also.
        
       | barefoot_coder wrote:
       | If this was interesting to you I suggest reading
       | https://journal.stuffwithstuff.com/2013/01/13/iteration-insi...
        
       | dnautics wrote:
       | After spending years in functional-land, I find the for loop (and
       | while loop) construct maddening; you have to maintain the state
       | of the entire enclosing scope system in your head, whereas
       | enumerable constructs like map and reduce close (ideally read
       | only) over variables in scope and if you need to keep state you
       | have to use reduce, which explicitly tracks what changes between
       | iterations and nothing "unexpected"... In a general sense, FP
       | enumerations are more explicit than control loops which are
       | implicit state machines.
        
         | peterthehacker wrote:
         | > After spending years in functional-land, I find the for loop
         | (and while loop) construct maddening; you have to maintain the
         | state of the entire enclosing scope system in your head.
         | 
         | This is only true if immutability is enforced. In js you see
         | map used to mutate variables outside the scope of the map
         | closure all the time.                 const o = {}       const
         | d = [1, 2, 3, 4]       d.map(i => o[i] = i**2)
         | 
         | Which is equivalent to this python.                 o = {}
         | d = [1, 2, 3, 4]       for i in d:         o[i] = i**2
         | 
         | The cognitive load is the same in both. The strength of pure FP
         | languages come from enforced immutability, but that constraint
         | often adds cognitive load in other ways.
        
           | dnautics wrote:
           | > constraint often adds cognitive load in other ways
           | 
           | In my experience (and others) those constraint only reduces
           | cognitive load, it _can_ increase actual performance load,
           | and can make certain algorithms  "basically impossible", but
           | you're also never actually writing those algorithms. When was
           | the last time you ACTUALLY used dykstra's A*? Come on, most
           | of us are writing SAASes, APIs/backends and basic frontends
           | here (yes, the rest of you do exist), and even for shitty
           | O(n^2) algos, your n is probably in the 10-20 range. Your bad
           | algorithm will not take down the server.
        
             | peterthehacker wrote:
             | I think most programmers would disagree with you on this.
             | Perhaps after enough FP experience the cognitive load that
             | comes from the language's constraints fade away, but I
             | haven't seen this in practice. FP is less commonly
             | understood and harder for the average dev to work with.
             | 
             | > Come on, most of us are writing SAASes, APIs/backends and
             | basic frontends here (yes, the rest of you do exist), and
             | even for shitty O(n^2) algos, your n is probably in the
             | 10-20 range. Your bad algorithm will not take down the
             | server.
             | 
             | This isn't related to the earlier point but I'll bite. This
             | thought process assuming "Your bad algorithm will not take
             | down the server" is a recipe for bad engineering.
             | 
             | For example, we had a bulk action (1-500 records) in our
             | API where the first implementation pulled all the data into
             | memory and processed the data in the request. This ended up
             | being disastrous in prod. It took down our server many
             | times and was tricky to track down because the process
             | would be killed when it maxed out memory.
             | 
             | The solution wasn't to switch languages or anything. It was
             | just to move the operation to our async worker queue and
             | stream through chunks of data to avoid running out of
             | memory. It cause a lot of headaches for devops that should
             | have never happened.
             | 
             | While you're right that there are many cases where n is not
             | large, engineers must consider how large n can be or
             | explicitly restrict n before pushing a bad algo to prod.
        
             | toxik wrote:
             | People write exploding time complexity code in bad places
             | all the time. I absolutely disagree with this sentiment
             | that it won't matter.
        
       | mgraczyk wrote:
       | Hmmm, I don't think this article accurately represents how people
       | write python since ~2005. I'm biased because I use python every
       | day, but there's a big objective mistake.
       | 
       | > Ruby flips the script, giving the objects deeper
       | customizability.
       | 
       | Not really, because python allows you to do things the Ruby way,
       | but I'm not sure the reverse is true.                   class
       | Stuff:           def __init__(self):             self.a = [1, 2,
       | 3, 4]                def __iter__(self):             for item in
       | self.a:               yield item
       | 
       | and you can write it more simply in this case.
       | def Stuff():           a = [1, 2, 3, 4]           for item in a:
       | yield item
       | 
       | What about the select and map example?                   class
       | Stuff:           def __init__(self):             self.a = [1, 2,
       | 3, 4]                def __iter__(self):             for item in
       | self.a:               yield item                def map(self, f):
       | for item in self.a:               yield f(item)
       | 
       | Which can be used just like the ruby example, with syntax of
       | similar ugliness.                   print(Stuff().map(lambda
       | item: item))
       | 
       | I think I could come up with a python example that maps 1:1 onto
       | pretty much any ruby example, but I don't think it's possible in
       | general to find ruby examples that map onto more complicated
       | python.
        
         | zedr wrote:
         | > think I could come up with a python example that maps 1:1
         | 
         | My take on it:                   class Stuff:             def
         | __init__(self):                 self._list = [1, 2, 3, 4]
         | @property             def each(self):                 for el in
         | self._list:                     yield el                  for
         | item in Stuff().each:             print(item)
         | 
         | It's even less verbose than the Ruby equivalent in the original
         | article, thanks to the indentation-defined blocks.
        
           | teddyh wrote:
           | AFAIK, there is _no_ reason to use the form "for el in
           | self._list: yield el", unless you are running Python 3.2 or
           | older.
           | 
           | Why not:                 each = self._list
           | 
           | Or, if you need to be able to re-assign self._list to a new
           | object:                 @property       def each(self):
           | return self._list
           | 
           | Or, if you for some reason need it to return an iterator:
           | @property       def each(self):         return
           | iter(self._list)
           | 
           | Or, if you _really_ want it to be a generator function:
           | @property       def each(self):         yield from self._list
        
         | softwaredoug wrote:
         | Author here! Very true, though I was trying to focus on
         | _idiomatic_ python that pushes logic into for loops, list
         | comprehensions, etc w / composable iterators through tools like
         | those found in itertools (zip, etc...)
         | 
         | Since Python doesn't have Ruby's blocks, you have to define a
         | non-trivial function as a free function, so it's less likely
         | you'll do this. (Python's lambda's are far more limited than
         | Ruby's blocks.)
         | 
         | You can also trick Ruby to return a new value every time a
         | method is called for iteration, but again my focus on what I
         | see as idiomatic in the two languages
        
           | gvx wrote:
           | Your Python example is far from idiomatic, though. If a
           | Python programmer were forced to write `Stuff`, they'd write
           | something like:                   class Stuff:
           | def __init__(self):                 self.a_list = [1,2,3,4]
           | def __iter__(self):                 return iter(self.a_list)
           | 
           | --- or do what the other person did, and make `__iter__` a
           | generator, which isn't necessary in this case but is much
           | more flexible.
        
       | tomp wrote:
       | Also known as "external" vs "internal" iterator.
       | 
       | Bottom line: external iteration is composable, internal iteration
       | isn't. Try implementing _zip_ or early stopping using internal
       | iteration (hint: you can 't).
        
         | gpderetta wrote:
         | You can if your language supports first class continuations!
         | (Which ruby has: yield work differently from python I think).
         | 
         | Then you can turn any internal iterator into an external one.
        
         | chubot wrote:
         | Yup this is a great article about it that I coincidentally re-
         | read yesterday:
         | 
         | https://journal.stuffwithstuff.com/2013/01/13/iteration-insi...
         | 
         | I find it easier to think of as "push-pull" though. In Python
         | and Go, you "pull" from iterators. In Ruby, JavaScript, and
         | Rust, you're "pushed" upon. You can do both styles in any
         | language, but there's one way that's favored.
         | 
         | To break the dilemma, you need 2 call stacks, which means
         | threads / coroutines / generators.
         | 
         | (There was a recent blog post about writing an iterator to do
         | the powerset calculation, which is a bit tricky, and made me
         | think of this post)
        
       | lottospm wrote:
       | I was never a fan of ruby until I had to use it at work.
       | 
       | I think my main superficial turn-off was indeed the non-
       | traditional loops and I was a fan of python whitespace
       | indentation.
       | 
       | But on the whole ruby just feels very coherent and language
       | features mesh very well together - it often feels like a true
       | lisp with self-style object orientation done very well [1].
       | 
       | Python otoh feels like an imperative language with classical OOP
       | added on and extended via magic methods.
       | 
       | See e.g. functional programming in python vs ruby with map,
       | filter, reduce and so on and how blocks and procs in ruby
       | interact with those vs lambdas in python.
       | 
       | I still use python often but have to say I tend to miss ruby when
       | I do.
       | 
       | [1] https://news.ycombinator.com/item?id=1803815
        
         | jimbokun wrote:
         | > In terms of programming-in-the-large, at Google and
         | elsewhere, I think that language choice is not as important as
         | all the other choices: if you have the right overall
         | architecture, the right team of programmers, the right
         | development process that allows for rapid development with
         | continuous improvement, then many languages will work for you;
         | if you don't have those things you're in trouble regardless of
         | your language choice.
         | 
         | "the right overall architecture, the right team of programmers,
         | the right development process that allows for rapid development
         | with continuous improvement" should be the working definition
         | of "agile" development in almost every context. Would save a
         | lot of confusion and pointless arguments about the true
         | definition of agile, kanban, extreme programming, or what have
         | you.
        
           | lostcolony wrote:
           | https://agilemanifesto.org/
           | 
           | Most of the things you list are things you may or may not get
           | right the first time; what makes something agile, at least,
           | when the term was first floated, was the ability for the team
           | to own the solution, say "this is not working" (be that
           | architecture, team structure, process, etc), and change it
           | easily. Agile was "ability to (rapidly) change", not
           | "adherence to a defined set of rules called 'Agile' ".
           | 
           | Most places don't actually seek out that definition of agile,
           | though. And so there's a lot of bikeshedding around "what
           | process is the one true way (and what can we add to it to
           | make it work when it fails us)" rather than "how do we
           | empower the teams to make their own decisions on how to best
           | be effective"
        
           | [deleted]
        
         | pkulak wrote:
         | It's always been the global functions in Python that put me
         | off. My brain just doesn't think like that. When I want the
         | length of an array, I look for a method on the array. Same with
         | map, and anything else.
         | 
         | That said... pretty small gripe in the grand scheme (no pun
         | intended, maybe) of things. When I've written projects in
         | Python, I've enjoyed it.
        
           | echelon wrote:
           | > It's always been the global functions in Python that put me
           | off.
           | 
           | I'd argue it's the inconsistency. Some things are methods,
           | some things are functions. The name choices are also a bit
           | boneheaded (eg. "dumps"). It's a lot like PHP in these
           | regards.
           | 
           | Ruby, on the other hand, is beautifully designed. But it
           | gives you a bazillion ways to skin a cat, and people love to
           | be "clever" with their cat skinning. In those cases, I
           | appreciate the utilitarian approach and "zen" of Python.
           | 
           | My biggest gripe with Python, though, is the totally 80s feel
           | of the version and package management. It's one of the worst
           | experiences in my daily engineering life. I can never know if
           | complex ML code will work on my system without thirty minutes
           | of patching things up.
           | 
           | I'll gladly use Python to get work done or Ruby for small
           | websites. But I'm looking for a new daily driver for
           | scripting that learns from the last thirty years of mistakes.
           | (Modern language, sensible packaging/dependencies/version
           | switch, optional static typing, and optional static binary
           | output.) Nim, perhaps?
        
             | Mikeb85 wrote:
             | Nim is nice but it's not dynamic so replacing a lot of the
             | use cases of Python/Ruby (or R) would be a pain. Like, I'm
             | not going to do any data munging in Nim when the others
             | exist, exploratory programming in Nim is also sub-optimal.
             | That being said, it's a nice language, way easier to write
             | than any other statically typed language I've used, I never
             | used Pascal/Delphi but Nim makes me appreciate that whole
             | family of languages a lot more (Nim is basically a modern
             | Pascal with Python's significant whitespace that compiles
             | to C).
        
               | goodpoint wrote:
               | Most Python translates into Nim quite easily, actually.
        
               | Mikeb85 wrote:
               | Not really. Proc vs def, let and var keywords, Pascal
               | style ranges and arrays, etc... I found C# is closer to
               | Nim when it comes to translating code than Python is. The
               | semantics are pretty different, especially since it seems
               | everyone uses numpy in Python for any sort of array
               | maths.
        
           | mixmastamyk wrote:
           | It's meant to be more readable by being closer to English:
           | 
           | > When I want the length of an array
           | len(array)
        
         | thedracle wrote:
         | I think in Python, I saw a good deal of adoption in the
         | Sciences early on.
         | 
         | I worked on a Physics experiment almost 15 years ago, there
         | were inklings of Python dripping in to doing numerical
         | analysis.
         | 
         | NumPy just using existing Fortran libraries is a good example
         | of it just being bootstrapped by the scientific community.
         | 
         | Most of these guys come from a Fortran background, and some
         | used the C++ ROOT framework.
         | 
         | Ruby was just too alien for them.
         | 
         | ROOT had made some excellent Ruby bindings, and I spent some
         | time showing some of my colleagues how much time they could
         | save using them, but they just found it to be too different.
         | 
         | It's sort of funny to think, because languages made for AI
         | originally like LISP used functional concepts like map,
         | collect, etc etc...
         | 
         | I think the AI trend using Python was boosted by numerical
         | analysis/computing libraries already being present.
        
         | a-dub wrote:
         | out of curiosity, how do people here feel about using python's
         | functional features in production code in the workplace?
        
         | petre wrote:
         | > was a fan of python whitespace indentation
         | 
         | That's what turns me off about Python (and f-strings), being a
         | Perl programmer. I've had to do some proof of concept for
         | reading smart cards and the Perl PCSC library sucks. So I did
         | it in Ruby, which like Perl has pack/unpack methods for
         | converting hex strings to binary and back. It's just great. Now
         | I have not two programs to read smart cards, one using the Ruby
         | smartcard library and another one using rubyserial and doing
         | serial communication directly with the card reader, with a
         | partial implementation of the T1 protocol, all in under 1k
         | lines of code.
        
         | pelasaco wrote:
         | Peter Norvig is always a pleasure to read, even on HN. Imagine
         | how precious is the history of HN Threads... i hope someday
         | someone compiles it in a consumable fashion.
        
         | sam0x17 wrote:
         | > I think my main superficial turn-off was indeed the non-
         | traditional loops and I was a fan of python whitespace
         | indentation
         | 
         | I've always been perplexed that this, a very small thing that
         | barely ever comes up since 99% of the time you are doing a for
         | each loop anyway, is the main turn-off for people learning
         | Ruby, when Python has those crazy weird if statements and
         | indentation-sensitivity
        
           | burnished wrote:
           | What do you mean crazy weird if statements? You can do one
           | liners, or if you don't your indentation is similar to what
           | you'd see in C++.
           | 
           | if condition: return expression if condition: foo = value
           | 
           | or
           | 
           | if condition: body statements elif condition: body statements
           | else: body statements
        
             | wizzwizz4 wrote:
             | Indent by                 two spaces
             | 
             | to get your line breaks to show up.
        
         | hk1337 wrote:
         | This doesn't have anything to do with Python but Ruby feels
         | more of a natural language for doing rapid application
         | development and active record than Laravel does.
        
       | racl101 wrote:
       | As a guy who uses a lot of languages and having started on C-like
       | languages I appreciate Python in that it doesn't make me work
       | terribly hard to get what I need to get done. I'm far from a
       | Python expert cause I don't use it exclusively.
       | 
       | In spite of all that, it's pretty intuitive, reads like pseudo
       | code. Plenty of data types and packages to get the job done.
       | 
       | The few times that I've touched Ruby, however, even though it's
       | succinct and beautiful language, I always have to find myself
       | relearning its super object oriented centric paradigm.
       | 
       | Everything takes 2-3 times as long to achieve and, arguably, not
       | that fast a language either.
       | 
       | I suppose if i worked on web app project exclusively, maybe Ruby
       | might be my language of choice, but I don't.
       | 
       | I'm sort of a jack of all trades master of none position (but
       | that's ok cause in my particular situation I get paid pretty well
       | for it).
       | 
       | So that's why I prefer Python at the moment.
        
       | mumblemumble wrote:
       | You could dig even deeper. The reason for these differences in
       | how the for loop works happen because Python comes from the
       | procedural tradition, while Ruby comes from the Smalltalk
       | tradition.
       | 
       | In other Smalltalk-inspired languages (but not Ruby), you can do
       | the same thing with an even simpler language feature: if-
       | statements. In procedural languages, including object-procedural
       | languages such as Java and Python, an if-statement is a core
       | language feature with its own syntax. In Smalltalk, conditions
       | are kicked out of the core language and into the standard
       | library. You have methods on the True and False objects that
       | either take an anonymous function and execute it or perform a
       | noop, or take two anonymous functions and decide which one to
       | execute.
        
       | fionic wrote:
       | Can ruby just die and go away already? But atleast it's not
       | another article stroking Rust's shaft...
        
       | behnamoh wrote:
       | Kinda opinionated and biased against Python.
        
         | stevev wrote:
         | In the end it is about preferences. It's going to be biased
         | given the subject at hand. I do prefer ruby. It's more object
         | oriented. The language really focuses on this. Example, the
         | plus sign symbol (+) can have Methods or even be a class or be
         | anything. That's Ruby.
        
         | inanutshellus wrote:
         | IMHO the author successfully remained neutral. Every point
         | about the way one language works was neither criticism nor
         | compliment, merely observation, and was immediately followed by
         | the other language's differentiating take on a particular
         | feature.
        
         | rpmisms wrote:
         | Kinda like Ruby :P
        
         | softwaredoug wrote:
         | Author here. Interesting! I personally write almost Python
         | exclusively and find Ruby to be awkward in many ways. I'm
         | learning Ruby, so if that comes through, maybe cause I'm
         | learning it and more excited about new concepts? :)
        
           | ewalk153 wrote:
           | I read it as the opposite of the GP.
           | 
           | You've highlighted the idiomatic differences in the language
           | design and how each lends itself to a different style of
           | thinking. Neat.
        
             | emi2k01 wrote:
             | May I know what GP means? It's the first time I see it.
        
             | drcongo wrote:
             | Same here. I thought it was well balanced and interesting.
        
         | ARandomerDude wrote:
         | So what? At a certain point in your career you should have
         | opinions about how problems should be solved.
        
       | StefanKarpinski wrote:
       | This feels a bit ironic given how much the for loop has been
       | villainized in numerical computing, by none other than languages
       | like Python--and Matlab and R--where for loops are so awfully
       | slow that you can only get performance if you avoid them like the
       | plague and write (sometimes awkward) vectorized code that pushes
       | all the for loops down into some C library (or C++ or Fortran).
       | Compared with, say, Julia, where people are encouraged to "just
       | write yourself a dang for loop"--because it's not only effective
       | and simple, but also fast. I guess what I'm saying is that it
       | feels like even though Python may embrace the for loop as syntax,
       | that affinity seems superficial at best, since if you care at all
       | about performance then Python immediately rejects the for loop.
        
         | a-dub wrote:
         | matlab has been jit compiled for years now, the "for loops are
         | slow" dogma is over.
         | 
         | numerical computing in python is kinda weird as it wasn't the
         | original purpose and the fast math libraries were bolted on as
         | an afterthought, but even then tools like numba do the same in
         | python, although there's a bunch of nuance in writing simple
         | enough python and hinting at the correct types for the
         | variables in order to get it to compile something reasonable.
         | 
         | julia's let's use strict types, jit compile everything from day
         | one and avoid locking approach is nice though.
        
           | toxik wrote:
           | After numba'ing several nontrivial pieces of numpy code in my
           | life, you might as well just rewrite it in nopython mode for
           | Cython unless it's very trivial stuff. Numba errors and
           | partial coverage of numpy is a huge time sink in my
           | experience.
        
         | anonpython wrote:
         | In Python numeric computing it's common for your outer loops to
         | be for loops and your inner loops to be vectorized
         | PyTorch/whatever.
         | 
         | I personally like being able to easily comprehend and control
         | what's being vectorized. Maybe it would be nice if my compiler
         | could automatically replace any inefficient loops with
         | vectorized equivalents, and I could think in whichever idiom
         | came more naturally to the problem at hand. But I don't think
         | there's anything too illogical about looping over epochs and
         | batches, and then computing your loss function with matrices.
         | Maybe I'm just used to a suboptimal way of doing things :)
        
           | StefanKarpinski wrote:
           | > Maybe it would be nice if my compiler could automatically
           | replace any inefficient loops with vectorized equivalents
           | 
           | The trouble is that a for loop is _much_ more expressive than
           | vectorized operations, so most for loops cannot be
           | transformed into vectorized equivalents. The reason
           | convincing people to write vectorized code works for
           | performance is that you 're constraining what they can
           | express to a small set of operations that you already have
           | fast code for (written in C). Instead of relying on compiler
           | cleverness, this approach relies on human cleverness to
           | express complex computations with that restricted set of
           | vectorized primitives. Which is why it can feel like such a
           | puzzle to write vectorized code that does what you want--
           | because it is! So even if a compiler could spot some simple
           | patterns and vectorize them for you, it would be incredibly
           | brittle in the sense that as soon as you change the code just
           | a little, it would immediately fall off a massive performance
           | cliff.
           | 
           | I guess that's actually the second problem--the first problem
           | is that there isn't any compiler in CPython to do this for
           | you.
        
         | mixmastamyk wrote:
         | With hindsight, the languages turned out to be a bit "too
         | dynamic" for their own good. Very few are changing variable
         | types often enough for that feature to be useful. The downside,
         | makes typing bugs possible/more likely, and slows down every
         | access. Par for the course, would say that slow loops are a
         | symptom not a cause.
        
       | nomoreusernames wrote:
       | are we still doing the whole y vs x thing with languages? why are
       | people wasting time on this? assembler wins. we already know
       | this. the rest is just laziness. /this is sarcasm.
        
       | Mikeb85 wrote:
       | Ruby is the first language I learned (apart from dabbling with
       | Visual Basic as a kid). I still reach for it to actually get
       | things done. One thing I always appreciated about it was the
       | consistency of everything being an object and how that allows you
       | to just chain methods together. Kind of reminds me of Lisp in
       | it's consistency (although in Lisp everything is data or a list,
       | not an object). Later I dabbled in Smalltalk (Pharo) just for fun
       | to try to understand what inspired Ruby's object model.
       | 
       | I've tried Python, hated the inconsistency of it, but then again
       | I never tried any C family language before learning Ruby. Calling
       | Each then feeding it a block feels more natural and consistent
       | than a for loop IMO, even if for loops are the standard
       | 'programming' way of doing it. It does make sense to use it as an
       | analogy to describe the differences between the languages.
        
         | peterthehacker wrote:
         | > I've tried Python, hated the inconsistency of it
         | 
         | Can you elaborate on this? I've written in both Python and
         | Ruby, but I'm not sure what you mean by this critique of
         | python. I wouldn't characterize either language as
         | "inconsistent."
        
       | dorianmariefr wrote:
       | This is not idiomatic ruby, you would do                   class
       | Stuff           attr_reader :list                def initialize
       | @list = [1, 2, 3, 4]           end         end
       | 
       | Then:                   Stuff.new.list.each { |item| ... }
       | Stuff.new.list.map { |item| ... }         Stuff.new.list.select {
       | |item| ... }
       | 
       | And if you want top-level each/map/select methods, you could do
       | delegate :each, :map, :select, to: :list
        
         | werdnapk wrote:
         | Including `Enumberable` and implementing `each` gets you all
         | those other methods for "free".                   class Stuff
         | include Enumerable                def initialize
         | @list = [1, 2, 3, 4]           end                def each
         | @list.each{ yield _1 }           end        end
        
           | [deleted]
        
           | gfunk911 wrote:
           | Exactly. It is an interesting idea, but it makes me question
           | the examples when `Enumerable` does not appear in the post.
        
         | rplnt wrote:
         | Wouldn't it be similar to use this in the python example?
         | class Stuff(list):            pass
         | 
         | kinda makes the article pointless. List obviously only serves
         | as an easy to understand example, not something you are
         | actually trying to implement.
        
         | retbull wrote:
         | From an entirely tongue in cheek perspective isn't all ruby
         | idiomatic.
        
         | bwilliams wrote:
         | It's also worth mentioning the Enumerable module that can be
         | mixed in to classes, which gives you `map`, `select`, and much
         | more for free by implementing `each`.
         | 
         | https://ruby-doc.org/core-3.0.2/Enumerable.html
        
         | softwaredoug wrote:
         | Author here, nice! TIL :)
        
           | stouset wrote:
           | I have written Ruby professionally for like fifteen years and
           | I have _not once_ witnessed a `for` loop.
           | 
           | I'm genuinely confused as to where you picked this up.
           | Enumerator and Enumerable are endemic.
        
             | xtracto wrote:
             | I would actually say that if I found a for loop in Ruby, it
             | will be a code smell.
        
             | halostatue wrote:
             | Almost twenty years here. I have definitely seen and used
             | `for` loops, but they are the _exception_ and usually used
             | in one-off scripts.
        
         | bullfightonmars wrote:
         | I thought the examples were just for illustration about how
         | iteration works in Ruby.
        
       | edmcnulty101 wrote:
       | This is a great article. I really enjoy reading articles
       | comparing two languages and these kinds of posts are not as easy
       | to come by as discussions about a single language.
        
       | bkovacev wrote:
       | I know it's not completely related to the thread - but how is
       | Rails vs Django in terms of performance? I'm getting subjective
       | information that Rails is much faster for APIs in terms of
       | response times, serialization speed etc. although I have been
       | using django for the past 7-8 years and have to say that the best
       | response times was 200-300ms w/o caching for some heavy json
       | responses. And something that should be blazing <100ms, still
       | takes 200ms to respond.
        
         | trimbo wrote:
         | There are too many factors to make a broad comparison in
         | milliseconds. Are you running the code on a RaspPi or on the
         | latest Zen3?
         | 
         | Serialization should be implemented in C so unless the JSON is
         | megabytes in size, whatever is happening here probably has to
         | do with the business logic implemented in Python. Python and
         | Ruby are equally slow when they aren't wrapping frameworks
         | primarily written in C (e.g. tensorflow), so choosing between
         | them is IMO a stylistic or compatibility choice.
        
       | revskill wrote:
       | def f():                   s = "Me too."              print(s)
       | 
       | # Global scope
       | 
       | s = "I love Geeksforgeeks"
       | 
       | f()
       | 
       | print(s)
       | 
       | This one returns `I love Geeksforgeeks`
       | 
       | Holy mogy, God bless someone doing codereview or inherit python
       | code from some bad developers.
        
         | tmerr wrote:
         | It prints "Me Too." then "I love Geeksforgeeks". What is
         | strange about this?
        
           | sgc wrote:
           | I would expect it to print "me too", because I would expect
           | the assignment in the function to change the global variable
           | when it is called, not create another variable.
        
             | fennecfoxen wrote:
             | Your expectation does not match the best practices that
             | language designers have converged on in this day and age.
             | No one can keep up with all the variables outside a given
             | function, and it's way to easy to cause confusing,
             | untraceable side effects. In almost every language,
             | variables introduced in methods like this will be lexically
             | scoped when the contrary is not otherwise indicated.
             | 
             | (Some of the older languages do differ, and usually best
             | practices in those languages include running linters that
             | yell at you to use explicit scopes.)
        
               | sgc wrote:
               | Then the function should not use the global variable at
               | all without explicitly indicating it. Printing 2
               | different variables when referring to a singular one is
               | not great behavior, and passes the smell test for
               | "confusing, untraceable" side effect to me. There should
               | also be a different syntax for initializing a variable
               | and changing a variable value, so that your intention is
               | obvious.
               | 
               | I can see the point a bit of added safety of basically
               | defaulting to a constant when declaring global variables,
               | although since I use older languages (but not python), I
               | have not run into that. I do find it humorous that the
               | global variable is semi-protected in python, while the
               | variable type is not.
        
               | orf wrote:
               | > Then the function should not use the global variable at
               | all without explicitly indicating it
               | 
               | Generally you use `global` to handle this.
               | 
               | > I do find it humorous that the global variable is semi-
               | protected in python, while the variable type is not.
               | 
               | It's not, there are no differences between them. Bindings
               | (names) are captured like most other languages, and
               | you're free to overwrite them if you wish.
               | 
               | In your example you bind a new string to an existing
               | name. I think it's pretty expected that this wouldn't
               | suddenly change the global object to a local one, that
               | would be madness.
               | 
               | But you're free to update the object _referenced_ if it's
               | mutable:                 def f():
               | thing.append(1)       thing = []       f()
               | print(thing)
        
               | sgc wrote:
               | Assigning is different than appending, and is the case
               | with a syntax ambiguity. But I'm also not a big fan of
               | the implicit `global` in your example if it's not
               | _always_ implicit.
               | 
               | Fine, we want a test by using `global` to make sure we
               | are doing what we intend when we assign, especially since
               | I see python does not even have true constants so there
               | is a basic lack of safety there. There should also be
               | similar tests before passing every variable with the same
               | name through our function and treating a single name as a
               | list of references without a keyword `all` or similar. Or
               | changing the type of a variable without explicit casting.
               | 
               | To me that lack of continuity just makes a language feel
               | idiomatic and buggy.
        
             | djmetzle wrote:
             | Oh no. oh no no. Do you have examples of languages without
             | the usual local scope binding semantics?
             | 
             | I think the example here is misleading and causing
             | confusion.
        
               | Macha wrote:
               | Javascript and C.
               | 
               | Neither are considered pinnacles of language design.
        
               | kevinmgranger wrote:
               | Javascript requires explicit variable declaration if
               | you're bringing it to a new scope, though. Which is to
               | say:                   var s = "Geeksforgeeks";
               | function f_global() {             s = "Me too";         }
               | // is different from         function f_local() {
               | var s = "Me too";         }
        
               | sgc wrote:
               | I like that better (and I realize I might be alone!). But
               | I don't like that you can declare a global variable in a
               | local function by omitting the var. It's a similar
               | needless ambiguity and frankly much more prone to errors
               | than the python local variable initialization. At least
               | there is strict mode.
        
               | chaxor wrote:
               | I believe Wolfram Mathematica has this terrible global
               | scoping by default.
               | 
               | It's not exactly a language that programmers refer to
               | often, as it's not really general purpose, but I had to
               | do some work with MRI data in it and I hated it mostly
               | due to the scoping.
               | 
               | An "I spent a decent bit of time rewriting the entire
               | lab's codebase in python" type of hate.
        
             | mcintyre1994 wrote:
             | Almost no programming languages will do that. JavaScript
             | will print "me too" twice if you don't use let/const, but
             | that's not recommended.
        
             | intrepidhero wrote:
             | Then Lua is for you! :-)
        
             | [deleted]
        
         | Kichererbsen wrote:
         | this is totally expected behavior... "Me too." is local to f...
         | what did you expect?
        
         | [deleted]
        
         | DangitBobby wrote:
         | This prints:                   Me too         I love
         | Geeksforgeeks
         | 
         | The s introduced in f is local to the function unless you use
         | the nonlocal or global keyword with it. To do otherwise seems
         | like a really bad idea to me...
        
         | trufas wrote:
         | The tradeoff here is that you don't have to explicitly declare
         | new variables (something like `var` in js). Most people who
         | work with python on a daily basis know you need the `global`
         | keyword to avoid shadowing.
         | 
         | I think it was a good choice in the sense that it discourages
         | mutable global state and makes the most common case more
         | concise.
        
         | friedman23 wrote:
         | this works exactly how I would expect it to?
        
         | kevinmgranger wrote:
         | You can explicitly reference a global variable if you want this
         | behavior:                   def f_global():             global
         | s             s = "Me too."             print(s)
         | print(s)         f_global()         print(s)
         | 
         | Will print:                   I love Geeksforgeeks         Me
         | too.         Me too.
         | 
         | Of course... please don't do global mutable variables.
        
       | qwertox wrote:
       | I have nothing to contribute to the content of this article, but
       | I really want to express my gratitude to the author.
       | 
       | I've been using Python for over a decade and installed Ruby once
       | or twice just to touch it, and I really like how this article has
       | managed to bring Ruby onto my radar, not as something which I
       | should use, but should appreciate.
       | 
       | For me it was just a Python alternative which some companies
       | really do like, but this article told me a bit about the beauty
       | of the language. Nice differences.
        
       | gilbetron wrote:
       | Seems odd to not talk about Python generators in an article about
       | this topic.
        
         | softwaredoug wrote:
         | Author here - True! though generators are a specialization of
         | iterators discussed in this article. I had considered it, but
         | didn't want to get too bogged down.
        
       | isaacimagine wrote:
       | At a deeper level, this comes down to internal vs. external
       | iteration, and, more generally, how languages weave together
       | concurrent threads of execution:
       | 
       | https://journal.stuffwithstuff.com/2013/01/13/iteration-insi...
        
       | jrochkind1 wrote:
       | This was definitely written by a pythonist! If I tried to write
       | it, as a rubyist, I'm sure I'd get some things about python
       | wrong. (I find it notable how _few_ people there are that are
       | actually familiar with both).
       | 
       | The standard alternative to `for` in ruby does involve `each` and
       | blocks... but definitely doesn't involve defining a custom `each`
       | method on your class... That is a specialty thing that most
       | developers have probably done rarely. Let alone defining it in
       | terms of `for`, which is just weird!
       | 
       | But the basic principle stated "Instead of passing data back to
       | the for loop (Python) you pass the code to the data (Ruby)" -- is
       | more or less accurate.
       | 
       | blocks -- syntactic support in the language for cleanly passing a
       | single in-line defined lambda/closure object as an argument --
       | are possibly the thing that are most special to ruby.
       | 
       | > Python builds on for-like constructs for all kinds of
       | processing; Ruby pushes other kinds of data processing work to
       | methods.
       | 
       | OK, maybe, although not totally sure what you mean.
       | 
       | > Ruby keeps going with its methods-first approach, except
       | instead of each we have a new set of methods commonly implemented
       | on collections, as below:
       | 
       | Um. I'm not sure where the author is getting this. It is
       | certainly possible, as shown, but definitely _not_ common to
       | implement `select` or `map` or other methods provided by
       | Enumerable directly on your custom class. It is a _bit_ more
       | common to implement `each` alone and let the `Enumerable` mixin
       | provide the rest. But even that I 'm not sure how "common" I'd
       | call it.
       | 
       | > Ruby, however, inverts this. Ruby puts object-orientation as
       | the foundation of the pyramid. Ruby contains the messy procedural
       | world in blocks, letting objects work with those procedural
       | blocks.
       | 
       | OK, true. The author is getting the details wrong, but I guess
       | their overall picture is still right?
        
         | kache_ wrote:
         | as a rubyistpythonista citizen, i agree
        
         | softwaredoug wrote:
         | > but definitely doesn't involve defining a custom `each`
         | method on your class...
         | 
         | Author here! Thanks for the feedback.
         | 
         | I suspected as much, and was more or less writing it to be
         | illustrative. Though I agree I am bad at Ruby :)
        
           | tinco wrote:
           | I don't think jrochkind is correct at all, if you want to
           | make a library that has a datastructure that you want to
           | implement custom iteration on, then it is most definitely
           | idiomatic ruby to implement `each` for it. Your article was
           | spot on in my opinion.
           | 
           | That said, in the almost 15 years I've been doing Ruby as my
           | preferred programming language, I think I can count the
           | amount of times I implemented a custom `each` method on one,
           | maybe two hands.
           | 
           | As an example of how `each` is idiomatic, consider the
           | Enumerable mixin: https://ruby-
           | doc.org/core-3.0.2/Enumerable.html if you implement `each` on
           | your class that mixin gives you all those methods (such as
           | select) for free.
        
         | chowells wrote:
         | > blocks -- syntactic support in the language for cleanly
         | passing a single in-line defined lambda/closure object as an
         | argument -- are possibly the thing that are most special to
         | ruby.
         | 
         | Too bad ruby stopped short of doing the trivial obvious thing
         | and just making blocks be regular values. Instead the language
         | is complicated by special syntax and functions for sending and
         | receiving blocks, and bizarrely limited by the inability to do
         | anything with a block literal other than send it.
         | 
         | Blocks were so close to being good. They managed the triple
         | flip with a double twist, but they couldn't stick the landing.
         | It's not quite a faceplant at the end, but it clearly shows how
         | much better they could have been.
        
           | gwright wrote:
           | > Too bad ruby stopped short of doing the trivial obvious
           | thing and just making blocks be regular values. Instead the
           | language is complicated by special syntax and functions for
           | sending and receiving blocks, and bizarrely limited by the
           | inability to do anything with a block literal other than send
           | it.
           | 
           | Blocks are syntactical structures. Your statement is like
           | saying that the parens and commas that are part of the
           | argument list should be "regular values".
           | 
           | I'm not sure what you want to do with a "block literal". If
           | you want to do something with the closure represented by the
           | block, then reify the block into an object to pass it around,
           | wrap it, introspect etc.
           | 
           | I think a lot of confusion about blocks in Ruby is really
           | inconsistent terminology. Using "block" to mean the syntactic
           | expression of a closure, i.e. part of the method call syntax
           | I think helps to disambiguate the syntactical nature of
           | closures from the closure reified into an object that you can
           | pass around, call, etc. (i.e. an instance of the Proc class)
        
             | chowells wrote:
             | > Blocks are syntactical structures. Your statement is like
             | saying that the parens and commas that are part of the
             | argument list should be "regular values".
             | 
             | Well, no. Know what else is a syntactic structure? Numeric
             | and string literals. People would never have touched the
             | language in the first place if Ruby required you to write
             | code like                   x = String("abc")         y =
             | Integer(123)
             | 
             | But people bend over backwards to explain why
             | z = { |x| x + 1 }
             | 
             | Is bad and shouldn't be allowed.
             | 
             | What?
             | 
             | The whole block vs proc thing is an artificial distinction.
             | It doesn't add value, it removes it.
             | 
             | (Yeah, I know about the difference in how they handle
             | return. This strikes me as an incredibly ad-hoc way to
             | address something they could have solved a lot more
             | elegantly.)
        
               | dragonwriter wrote:
               | > But people bend over backwards to explain why "z = {
               | |x| x + 1 }" is bad and shouldn't be allowed.
               | 
               | Really? I've never seen anyone bend over backward to
               | explain it as bad, mostly just that that's the way it is,
               | there are tradeoffs either way, and its not worth
               | changing.
               | 
               | > The whole block vs proc thing is an artificial
               | distinction
               | 
               | Presumably, you mean _lambda_ vs. proc. (Both of which
               | are defined using blocks, and procs are what using  &
               | syntax in a function signature causes a passed block to
               | be reified into.)
               | 
               | But, yes, all distinctions are creations of humans,
               | especially all distinctions within human creations like,
               | say, programming languages. So "artificial distinction"
               | is a meaningless descriptor when we are talking about
               | things in a programming language.
        
               | jdc wrote:
               | Would it be fair to say you don't want to write this?
               | z = ->(x) { x + 1 }            z.call(y)
        
               | fallingknife wrote:
               | I don't see why there is this distinction where lambdas
               | have to be called differently than functions.
        
           | dragonwriter wrote:
           | > Too bad ruby stopped short of doing the trivial obvious
           | thing and just making blocks be regular values.
           | 
           | Abstractly, I kind of see the point, in practice, I don't see
           | it makes much difference, and given Ruby's two flavors of
           | function-like objects, it seems to work out for the best.
           | 
           | > the inability to do anything with a block literal other
           | than send it.
           | 
           | But sending lets you do anything else you'd want to to do
           | with it. Specifically, to get either of the flavors of
           | callables, you pass it to "proc" or "lambda", and then use
           | the result.
        
           | hvis wrote:
           | Not having every block be a separate value makes it easier
           | for the Ruby VM to optimize such code without escape analysis
           | (which is something that's pretty hard to do in this
           | language).
           | 
           | Don't have to allocate the container => don't need to use the
           | heap one more time. Nor access the block's code through that
           | indirection.
           | 
           | Same thing with methods: they aren't objects, but you can
           | create an object pointing to a method any time you need one.
        
           | hnov wrote:
           | What do you mean by inability to do anything with a literal?
           | You can capture it, turn into a variable, turn it into a
           | "lambda" (make next, break, return local), use it like
           | callbacks are used in any other language. Blocks are a bit
           | special in that they go in their own slot for message sends
           | (method calls) which allows the syntax to be unambiguous and
           | the yield keyword allows for optimized calls to passed in
           | blocks because methods that don't reference a block as data
           | don't have to worry about the block outliving the stack
           | frame.
        
           | masa331 wrote:
           | You have that with procs and lambdas
        
         | jrumbut wrote:
         | I didn't think the author was saying you should always
         | implement a .each method. I thought the implementations were
         | for demonstration purposes, that .each is a method like any
         | other that can be overridden.
        
           | greenie_beans wrote:
           | that's how i read it too, for the purpose of the technical
           | demonstration
        
         | pas wrote:
         | It's the norm in Scala too.
         | 
         | And it's a bit overdone in (older) JS (libraries).
        
           | jrochkind1 wrote:
           | I don't know scala, but the notable thing about ruby vs JS is
           | how ruby provides _syntactic_ support for passing an inline-
           | defined function.
           | 
           | In JS:
           | someObj.someMethod(function(something) {            something
           | });
           | 
           | Compare to ruby with the block arg:
           | someObj.someMethod do |something|           something
           | end
           | 
           | That `do` is a special syntactic thing avaialble for passing
           | a single inline-defined closure arg. (You _can_ pass one held
           | in a reference instead of inline-defined, but it actually
           | takes an extra somewhat obtuse step -- the syntax is
           | optomized for inline-defined).
           | 
           | This "affordance" says "Yes, we make it really easy to do
           | this, the stdlib does it a lot, please consider doing it all
           | the time in your own code", which goes along with some of
           | what the OP is discussing. Why we write `collection.each
           | {|something| something}` (the braces are an alternate way to
           | pass a block) instead of `for something in collection do...`
        
             | pas wrote:
             | In Scala it's called blocks [0], and if a function/method
             | expects one you can provide it inline, eg:
             | myList.foreach { x => doTheThingToTheThingEvery(x, 100
             | millis) }
             | 
             | Of course it's sometimes a bit too much. It starts out cute
             | [1] and handy [2][3][4], then [5] ... [6] :)
             | 
             | [0] https://docs.scala-lang.org/tour/basics.html#blocks
             | 
             | [1] https://scastie.scala-lang.org/tpfgE80WTc6QQaHslraJag
             | 
             | [2] https://www.playframework.com/documentation/2.8.x/Scala
             | Actio...
             | 
             | [3] https://stackoverflow.com/q/50370202/44166
             | 
             | [5] https://www.playframework.com/documentation/2.8.x/Scala
             | Actio...
             | 
             | [6] https://miro.medium.com/max/784/1*EMiSTuRIxUPCzWOgLkiXo
             | A.jpe...
        
             | hnov wrote:
             | I think the crucial difference is that a block can control
             | the flow of the enclosing frame, somewhat analogously to
             | python context managers. For example                 def
             | get_widget         with_lock do           return @widget #
             | returns from the method call         end       end
             | def update_interesting_gadget         @gadgets.each do |g|
             | g.with_lock do             if g.is_theone?
             | g.update               break # breaks the enclosing each's
             | while loop             end           end         end
        
             | Brybry wrote:
             | In modern JS wouldn't you use an arrow function?
             | someObj.someMethod(something => something);
        
               | didroe wrote:
               | The main advantages of Ruby blocks over that approach
               | are:
               | 
               | - they have special control flow that interacts with the
               | method call or surrounding function. ie. calling `break`
               | in `something` can early return from `someMethod`, or
               | calling `return` will return from the function containing
               | the `someMethod` call (blocks use `next` to return from
               | them)
               | 
               | - due to using separate syntax / being a separate
               | language construct, there is far better ergonomics in the
               | presence of vargs or default values
               | 
               | Take this contrived example for instance:
               | def some_method(a = 42)           b = yield
               | puts "Hey #{a} #{b}"       end            some_method do
               | break       end
               | 
               | In JS you would have to something horrible like this:
               | const breakSomeMethod = {}; // Could alternatively use an
               | exception            function someMethod(one, two) {
               | var f, a;         if (typeof one == 'function') {
               | f = one;           a = 42;         } else {           a =
               | one;           f = two;         }              var b =
               | f();         if (b === breakSomeMethod) {
               | return;         }              console.log(`Hey ${a}
               | ${b}`)       }            someMethod(() =>
               | breakSomeMethod);
        
         | Buttons840 wrote:
         | Python and Ruby are quite different when writing code, but have
         | almost the exact same technical abilities and limitations in
         | the grand scheme of things. I've always felt there's little
         | reason to learn both.
        
           | OJFord wrote:
           | Yes exactly, you're not really going to learn anything
           | (syntax aside) by picking up the second, unless you needed it
           | for work reasons or whatever there's surely several other
           | paradigmatically different languages that will be more
           | interesting/instructive to learn.
        
       | stevev wrote:
       | Developer happiness, I believe, was one of the main pinnacles and
       | design choice of Ruby according to Matz. Meaning he wanted
       | developers to enjoy coding in Ruby.
        
         | gilbetron wrote:
         | I think there are just multiple kinds of developers. I've
         | programmed a fair amount, professionally, in Smalltalk and
         | Ruby, and quite a bit in Python (along with lots of other
         | languages). If I squint I can see why some kinds of developers
         | like Smalltalk and Ruby more, but I am not that kind of
         | developer. Python is better (for me) for small to medium
         | projects, and then I prefer strict typing for larger projects
         | (currently Go, which I'm ok with).
         | 
         | But I know plenty of people that love, love, love Ruby and I
         | respect them as developers, but what we enjoy in a language is
         | just different.
        
           | TheRealDunkirk wrote:
           | > I think there are just multiple kinds of developers.
           | 
           | I've seen this very glaringly. I love Ruby and Rails, and
           | want to leverage the stack as much as is reasonable. I've
           | come to realize that there are people who _really would_ like
           | to write 100-200 lines of Java for every line of Ruby I
           | write, just so that they  "have control." I think they have
           | no idea what they're talking about, but I have no influence
           | over them. The fact that I'm writing my 3rd application in 6
           | years which does something that entire teams of outsourced
           | developers could not do is, surely, just coincidence.
        
         | thanatos519 wrote:
         | I believe it, and it shows! Coming from a longtime Perl
         | background and switching to Python (which has its pros and
         | cons) and then finally trying out Ruby, I felt like Ruby was
         | what Perl should have become.
        
           | kbenson wrote:
           | Ruby was heavily inspired by Perl. I know a few people that
           | moved from Perl to Ruby and felt quite a home. (Personally,
           | my path away from Perl is likely to be to TypeScript on Deno,
           | since if I'm going to make a change I might as well pick up
           | some _major_ benefits along with the normal set of trade-
           | offs).
        
       | xfalcox wrote:
       | > Ruby, however, inverts this. Ruby puts object-orientation as
       | the foundation of the pyramid.
       | 
       | I'm doing some work using Jupyter/Python and as a Rubyst on my
       | day job this is definetly how I felt!
       | 
       | Since data science has so many data manipulation, the move from
       | objects controlling the iterarion from language reserved words
       | doing it was sooo weird at first.
        
       | kazinator wrote:
       | TXR Lisp:                 $ txr stuff.tl        1       2       3
       | 4            $ txr stuff-cons-like.tl       1       2       3
       | 4            $ cat stuff.tl        (defstruct stuff ()
       | (vec (vec 1 2 3 4))         (:method length (me) (len me.vec))
       | (:method lambda (me index) [me.vec index])         (:method set-
       | lambda (me index val) (set [me.vec index] val)))            (each
       | ((x (new stuff)))         (prinl x))            $ cat stuff-cons-
       | like.tl        (defstruct stuff ()         (list (list 1 2 3 4))
       | (:method nullify (me) me.list)         (:method car (me) (car
       | me.list))         (:method cdr (me) (cdr me.list)))
       | (each ((x (new stuff)))         (prinl x))                 Quit
       | with :quit or Ctrl-D on an empty line. Ctrl-X ? for cheatsheet.
       | 1> (disassemble (compile-toplevel '(each ((x (new stuff))) (prinl
       | x))))       ** expr-1:1: warning: new: stuff does not name a
       | struct type       data:           0: stuff       syms:
       | 0: struct-from-plist           1: iter-begin           2: iter-
       | more           3: iter-step           4: iter-item           5:
       | prinl       code:              ;; manually annotated!
       | 0: 2001000A gcall t10 0 d0  ;; t10 <- (struct-from-plist 'stuff)
       | 1: 04000000           2: 20010009 gcall t9 1 t10  ;; t9 <- (iter-
       | begin t10) ; t9 is iterator           3: 000A0001           4:
       | 20010006 gcall t6 2 t9   ;; t6 <- (iter-more t9) ; more items?
       | 5: 00090002           6: 3800000F if t6 15        ;; if t6 not
       | nil continue, else goto 15.           7: 00000006           8:
       | 2001000A gcall t10 4 t9  ;; t10 <- (iter-item t9) ; get item
       | 9: 00090004          10: 20010006 gcall t6 5 t10  ;; t6 <- (prinl
       | t10)          11: 000A0005          12: 20010009 gcall t9 3 t9
       | ;; t9 <- (iter-step t9) ; step iter          13: 00090003
       | 14: 34000004 jmp 4           ;; repeat          15: 10000000 end
       | nil         ;; terminate, yielding nil       instruction count:
       | 9       #<sys:vm-desc: 14fcb80>
        
         | [deleted]
        
       | zitterbewegung wrote:
       | In my opinion it doesn't matter which language you use as long as
       | it's the right tool for the job . Usually this goes for
       | familiarity of either who you can hire and or you.
       | 
       | Python has some obvious syntax weirdness and Ruby looks like it
       | encourages more DSL and that might make it closer to lisp.
       | 
       | But, Python has so many more libraries and a good editor can help
       | you manage the white space indentation . Also Python scales well
       | too.
       | 
       | To be honest I rather have some kind of different framing of
       | this. Instead of an argument it could have been a comparison but
       | that doesn't get people to click the link.
        
         | FpUser wrote:
         | >In my opinion it doesn't matter which language you use as long
         | as it's the right tool for the job"
         | 
         | And "the right tool for the job" is of course language one
         | knows and loves ;)
        
       | lern_too_spel wrote:
       | The very first code block has a fundamental error. It allows only
       | one iterator per Stuff instance.
        
       | DFHippie wrote:
       | The discussion of Ruby could use a mention of Enumerable.
       | 
       | https://ruby-doc.org/core-3.0.2/Enumerable.html
        
         | softwaredoug wrote:
         | Author here, wow that's great! I am still learning Ruby (hence
         | the article to try to grok things).
        
           | usefulcat wrote:
           | If one is having to implement an each or map or select method
           | in ruby, then I can definitely appreciate the differences you
           | point out. Even though I've been using ruby a long time, it
           | would frankly still be a bit of a pain to have to remember
           | the details of how yield works.
           | 
           | Because Enumerable provides so much functionality, I almost
           | never need to implement methods like those. So what I really
           | care about a lot more are the ergonomics of _using_ each,
           | map, select, reject, all?, any?, etc, which I use very
           | frequently.
        
           | pelasaco wrote:
           | as suggestion, regarding the following code:
           | 
           | Stuff.new().each do |item|                 puts item
           | 
           | end
           | 
           | In idiomatic ruby, we drop this parenthesis, so the code will
           | look like Stuff.new.each ...
        
             | hirundo wrote:
             | puts Stuff.new.map(&:to_s).join("\n")
        
         | ericwood wrote:
         | Enumerable is one of my favorite aspects of the Ruby standard
         | library! I've been writing it off and on for quite some time
         | and still discover incredibly useful and elegant functions in
         | there. It's incredibly rare to not find what I need when
         | working with collections.
        
         | busterarm wrote:
         | Indeed.
         | 
         | The real magic is when you start doing things like:
         | 
         | some_enum.collect(&:+)
         | 
         | This simple line will do everything from sum numbers to
         | concatenate strings, etc.
        
           | mikepurvis wrote:
           | Not a rubyist, but I love that collect() made it into Rust,
           | too. I feel like it takes the role that generators have in
           | Python, but is much easier to reason about in terms of the
           | control flow and what data is being passed where.
        
             | dragonwriter wrote:
             | Enumerable#collect is just an eager version Python map() in
             | method rather than function form (#map is a avaulable as an
             | alias for #collect); it is a little more concise for when
             | the function you are mapping over is a method on the object
             | being processed (even more than Ruby's normal advantage
             | with lambdas), but not fundamentally more powerful.
             | 
             | (Enumerable#lazy gives you an object where collect/map is,
             | and other merhods are, lazy rather than eager.)
             | 
             | > I feel like it takes the role that generators have in
             | Python
             | 
             | Ruby has generators, though in either Ruby or Python
             | map/collect (the lazy versions, in Ruby) can be used for
             | some of their uses, since it basically produces a generator
             | from an input collection or generator and a mapping
             | function.
        
               | halostatue wrote:
               | #collect is an alias for #reduce, not #map, making it
               | like Python reduce().
        
               | dragonwriter wrote:
               | > #collect is an alias for #reduce
               | 
               | It's not, though. (If it was, we could write about its
               | equivalence with Python's [from functools in py3, core in
               | Py2] reduce function, though, saying much the same thing
               | as about the actual equivalence with map upthread, except
               | reduce/inject is less often a substitute for generators
               | than map/collect.)
               | 
               | #map is an alias for #collect. [0]
               | 
               | #reduce has the alias #inject. [1]
               | 
               | [0] https://ruby-
               | doc.org/core-3.0.2/Enumerable.html#method-i-col...
               | 
               | [1] https://ruby-
               | doc.org/core-3.0.2/Enumerable.html#method-i-inj...
        
               | pansa2 wrote:
               | Then should the example in the above comment,
               | `some_enum.collect(&:+)`, be using reduce instead of
               | collect?
        
         | benbristow wrote:
         | C# .NET implements quite a lot of these too via LINQ, albeit
         | with different names (e.g. map is called Select).
         | 
         | https://docs.microsoft.com/en-us/dotnet/api/system.linq.enum...
        
           | TheRealDunkirk wrote:
           | Oy vey, LINQ. Meshuggeneh. Nothing like dropping COMPLETELY
           | out of your language's idiom for a line or two to implement
           | some Enumerable-like behavior.
        
             | benbristow wrote:
             | I'd argue when I'm writing C# in a web-development setting
             | (ASP APIs etc.) that a large majority of the code is LINQ.
             | Especially if using something like Entity Framework. It's
             | really just part of the language now.
        
               | TheRealDunkirk wrote:
               | A line of LINQ looks and works nothing like a line of C#,
               | and the difference is even worse in VB. Whether it's
               | "part of the language" or not, it's a fundamentally
               | different way of writing logic than the base language
               | you're writing around it. I agree it's completely coupled
               | with EF, but I hate EF too. (After you've used
               | ActiveRecord for over 10 years, any other ORM just feels
               | like a ball and chain, but that's another topic.)
        
               | maleldil wrote:
               | Are you using the query syntax instead of the method
               | syntax? Because the method syntax is completely in line
               | with normal OO code (it's just a bunch of chained
               | methods). I'll agree that the query syntax is weird, but
               | I haven't seen it used much.
        
               | yumaikas wrote:
               | > A line of LINQ looks and works nothing like a line of
               | C#
               | 
               | Maybe if you're using the query form, but if you're using
               | the method form, I'd argue that they work together very
               | nicely.
               | 
               | For me, at least, I've found embracing Lambda expressions
               | to be very helpful in a number of cases, between Linq, or
               | writing my own code for doing things like rendering
               | tables (long story there).
               | 
               | Granted, I learned C# right after LINQ was released, but
               | I'd still argue that LINQ is well worth having around.
        
             | gfodor wrote:
             | It is interesting that this kind of context switch for some
             | programmers is a breath of fresh air, and for others its a
             | horrible hack.
             | 
             | I think the fact this is not an objectively good or bad
             | thing has led to a lot of the tension we see in other
             | aspects of programming.
             | 
             | I think a connected issue is tooling: if you presume a
             | certain floor on tooling, more 'advanced' things like
             | multiple composed domain languages become much more
             | feasible since the tooling can remove the need for
             | memorization and help guide construction without fear of
             | errors. But if you presume all tooling beyond text editing
             | should be regarded as non-essential at best and superfluous
             | at worst, it greatly constrains the set of things in
             | programming one can consider sane or not. I do wish we had
             | more projects experimenting + getting traction with "high
             | tooling, high abstractions, high domain mixing" so we could
             | get more literacy around these methods, but I think the
             | vast majority of programmers today have abandoned these
             | kinds of ideas as good or worthy of further development. On
             | pure interia alone, I long ago capitulated to the idea we
             | will be stuck in text editors and having heated debates
             | over typographical syntax for programming languages for the
             | foreseeable future.
             | 
             | People working on things like LINQ and more advanced domain
             | mixing like MPL or (where I once worked) Intentional
             | Software run up against lots of resistance from people I
             | think probably due to some fundamental differences in
             | assumptions, but I'm not really sure what the deepest
             | differences are.
        
             | ziml77 wrote:
             | Are you talking about the `from x in y select x.z` syntax
             | or the `y.Select(x => x.z)` syntax? Because the latter is
             | the one I most commonly see, and it fits in fine with the
             | rest of the language.
        
             | metaltyphoon wrote:
             | Out of idiom? Extension methods, the way LINQ is
             | implemented, has been introduced in C# 3
        
       | dragonwriter wrote:
       | While people have pointed out that this is not idiomatic:
       | class Stuff         def initialize           @a_list = [1, 2, 3,
       | 4]         end              def each           for item in
       | @a_list             yield item           end         end
       | end
       | 
       | I haven't seen anyone point out the deepest reason why: unlike in
       | many languages, "for" is not a basic, low-level construct in
       | Ruby, it is syntax sugar. That is:                 for x in y
       | x.stuff         more_stuff       end
       | 
       | is sugar for:                 y.each do |x|         x.stuff
       | more_stuff       end
       | 
       | (Also, for quite a long time, for...in also had runtime overhead
       | , so it was _slower_ syntactic sugar.)
       | 
       | for...in is rarely used (it is syntactic sugar that is neither
       | more concise bor more specific in intent, but more familiar to
       | people with experience in non-Ruby languages), and mostly on
       | scripts that consume but don't implement collections. If you are
       | getting deep enough to be implementing an #each method for a
       | custom collection, that's an odd place to use for.
        
         | saghm wrote:
         | I'm not sure if this was ever changed, but I remember running
         | into something surprising regarding variable scoping with for
         | loops (I think it was during an internship in 2014, so I think
         | Ruby 2.x with some relatively low value for "x"). Unfortunately
         | I can't remember exactly what it was; I think it was something
         | about variables defined in the for loop still being in scope
         | afterwards? That doesn't seem like it should be the case if
         | it's just syntactic sugar for `each` though, so I might be
         | remembering wrong
        
           | dragonwriter wrote:
           | Yes, for loops (and similar statements) don't introduce
           | scope, so its not quite simple sugar for .each, and there are
           | a few cases where you might use them over each for that
           | property (if you are mutating values that you want to access
           | afterward, it can be more concise than each) but they are
           | mostly anti-idiomatic.
        
       | petercooper wrote:
       | I'm a longtime Rubyist who's dabbling in Python a fair bit
       | nowadays due to the growth of the ecosystem and the libraries
       | available. I find Python enjoyable to work with but the thing it
       | makes me love more about Ruby is how in _most_ cases you think
       | about objects and the capabilities of those objects, whereas in
       | Python it feels like a crapshoot (I 'm sure it's _not_ , it's
       | just how it feels as a dabbler) as to whether you pass objects
       | into a function (e.g. `len`), use methods on objects, or whether
       | to even think of OOP at all :-)
       | 
       | Now, that's not entirely a negative, I quite like Python's
       | freewheeling "do whatever works and feels best for you" ways, but
       | that feels somewhat contrary to the Zen of Python's _" There
       | should be one-- and preferably only one --obvious way to do it."_
       | (!)
        
         | vikingerik wrote:
         | Those last aren't mutually exclusive. The idea is that there
         | should be one obvious way, such that you'll find that what
         | works and feels best is that way. The idea is that you will
         | simply want to comply, not to demand that you do.
        
         | mixmastamyk wrote:
         | https://news.ycombinator.com/item?id=29202044
        
       | gorgoiler wrote:
       | Prescient. I found myself wanting to pass a block of code to a
       | Python class today, like what I used to do when I wrote Ruby. I
       | _very_ nearly hacked it up using cursors and an iterator but I
       | saw sense before birthing a monster.
        
       ___________________________________________________________________
       (page generated 2021-11-12 23:00 UTC)