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