4 ms·
I'm a big Ruby fan, or at least I used to be. I had a hard time learning the language, because there are just too many ways to do the same thing. I would always
by syspec 5y ago
I'm a big Ruby fan, or at least I used to be. I had a hard time learning the language, because there are just too many ways to do the same thing. I would always second guess my memory and be forced to look up even the simplest most. common things.
This is a good example of that:
> There is more than one way to call a lambda function:
my_lambda = lambda { puts "hello" }
my_lambda.call
my_lambda.()
my_lambda.[]
my_lambda.===
---
I'm still a fan of the language, but I hope going forward they tone down the idea of N ways to do the same thing. It also seems to bite the language designers in the butt, as they end up running out of symbols for new concepts.
- mooreds 5y agoI think it is related to ruby's inheritance from perl, which made TMTOWTDI (there's more than one way to do it) a guiding principle.
- freedomben 5y agoProjects like Rubocop go a long way to help with this. I try to follow the bbatsov style guide and use rubocop for automatic fixes.
- halostatue 5y agoAs someone who has been doing Ruby for 20 years, the bbatsov style guide is bonkers. Rubocop can be good, but it needs too much configuration to make it not follow bbatsov guide — which is simply wrong on a number of levels. Recently, I’ve started using https://github.com/testdouble/standard https://github.com/testdouble/standard as a wrapper around Rubocop. It is both less and more opinionated than Rubocop, but aside from two style choices (`%w[]` — using `[]` there is legal but not idiomatic Ruby IMO; I also prefer terminal dots rather than leading dots (e.g., `foo.\nbar` instead of `foo\n.bar`), it doesn’t bother me nearly as much as Rubocop’s defaults do. There are many things that Rubocop gets wrong in its defaults. The absolute single #1 thing it gets wrong is markers. It sort-of supports the only useful distinction, laid out by Jim Weirich (`foo {}` when block return values are used such as `[…].map {…}`; `foo do end` for blocks without a meaningful return such as `[…].each do … end`). However, its "semantic" mode does this in the least-useful-way possible, by looking at the method names before the block. It may be the only way that Rubocop can do it, but…
- neon_electro 5y agoEchoing the sentiment of freedomben's comment, the Ruby Style Guide[1] is a great community consensus on which "one way" to pick. [1] https://rubystyle.guide/ https://rubystyle.guide/
- Lapsa 5y agowait what? `.()` is a thing? thank you, internet dude. I found that `.call` a bit verbose and annoying.
- ferdowsi 5y agoOddly representative of the prevailing Ruby attitude to prefer saving two characters over something absolutely explicit and informative like `.call`
- emmelaich 5y agoYou could be me. Exasperating for someone coming from C/Python/Lisp/other lambda makes lambdas but not really because you need `call` so you need to know they're a lambda. Ah yeah Proc a bit like lambda but also not really. And not until version 2.blah.... Aaargh!
- woodruffw 5y agoThat's because Ruby is really a Smalltalk in disguise :-) But seriously: Ruby has powerful message passing, blocks, and runtime introspection that address the same niches as real lambdas in other programming languages. They're difficult features to get used to from a Python (or Lisp) background, but leaning into them reveals much of Ruby's hidden beauty.
- llamataboot 5y agoyou either love meta programming or you hate it, and if you love it enough, you also sometimes hate it :D
- vidarh 5y agoLambda makes Proc instances because Ruby is purely OO and every value you can get is an object. It might be nice if there were syntax that allowed you to treat an object as if it was a method, but it'd be tricky because of other Ruby syntax. > Ah yeah Proc a bit like lambda but also not really. lambda/-> creates a Proc instance (with an appropriate flag set). The distinction between lambda vs (lower case) "proc" is tricky until you consider that a "proc" is effectively just a shortcut to return a block as a value. The semantics of a block reified into a Proc instance by giving it a name, and a Proc instance created with "proc" are the same. So you could implement your own "proc": def make_proc(&block) block end (in fact the above example is from the Ruby docs)
- petercooper 5y agoI find it amusing because I'm a Rubyist who has been trying to pick up some Python and I have exactly the same problem. There seems to be several ways to do everything (particularly around working with containers, lists, dicts, etc.) and not a lot of consistency as to the approach taken a lot of the time (e.g. functions versus methods on objects).
- solatic 5y agoDid you read Eloquent Ruby? Having many ways to do the same thing is intentional in Ruby's design, to try and promote a coding-as-literature style, whereby the language gives you the tools to write code that is as close to plain English as possible. This is explicitly why Ruby includes "unless" in place of "if not". The more ways that there are to express the same idea, the more tools the programmer has to write code that is easily readable and understandable by others. Human comprehensibility + a comprehensive test suite = long-term maintainability.
- deleted 5y ago[deleted]
- vidarh 5y agoI don't think this is a good example. E.g. "===" is supported because it's used in case ... when ... matching, and so it allows you to create lambda's that can be used in "when" clauses. You're not really expected to use it to explicitly call a lambda, and using "===" rather than "behind the scenes" in the form of a case/when is rarely done. Meanwhile "[]" allows you to use a lambda in the same circumstances as an array or hash without writing different code depending on type. Both are there as part of the Proc interface because they have significant impact on the usability of Proc instances by allowing them to be used in more contexts without special treatment. Attention paid to things like that is part of what makes Ruby pleasant to use. ".call" vs ".()" is left, and maybe having just one of them would be ok, especially as frankly I can't recall the last time I saw ".()" used on a lambda "in the wild" (I'm sure people are using it, just not in any code bases I've looked at recently), but they serve different purposes than "===" and "[]" even though their effect for Proc instances are the same.