8 ms·
Ruby Blocks
- stonecharioteer 1y agoI've been very taken by Ruby and how it uses blocks everywhere! This is an article I wrote just to emphasize that.
- pjmlp 1y agoHave a look at Smalltalk blocks, or FP languages, to see where Ruby's inspiration comes from.
- frou_dh 1y agoAn interesting thing about Smalltalk and Ruby blocks is that they aren't just anonymous functions/lambdas, right? i.e. if you 'return' / '^' in a block, it's the context around the block that you return from, not just the block itself? That's what struck me about both of them when I was used to thinking in basic Lisp terms.
- pjmlp 1y agoYes, that is why that behaviour is known as closures. Also why in languages like C++, you get to control what is captured from the calling context.
- vidarh 1y agoAs much as I love Ruby (and I love it a lot - I right now have Claude fix eigenclass compilation issues in another window in my long-languishing Ruby compiler hobby project), this is also the root of a lot of confusion. Ruby has two types of closures: lambda's, where return returns to the caller, and proc's/blocks where return acts as a return in the defining scope. Beginners often struggle with the distinction between lambda and block, and often will be wildly confused if they ever see "proc" in the wild. Yet lambda and proc both return a Proc object, and if you take the value of a block, you also get a Proc object... Just with different return semantics... It'd be nice if this was a bit clearer.
- stonecharioteer 1y agoMy next post, which is on loops, is about the common stuff with smalltalk as well!
- vidarh 1y agoRuby is basically what happens when Smalltalk and Perl (compare the command line and the "$"-variables for example) has a baby and it inexplicably looks much better than both parents.
- janfoeh 1y agoI discovered Ruby (through Rails) about twenty years ago on the dot. Coming from Perl and PHP it took me a while, but I remember the moment when I had the same realisation you did. I still love this language to bits, and it was fun to relive that moment vicariously through someone elses eyes. Thanks for writing it up!
- stonecharioteer 1y agoI'm glad folks are having fun reading this. I want to write a few more articles, particularly dissecting the internals of Ruby and how amazing it feels.
- PufPufPuf 1y agoTake a look at Kotlin, it perfected this idea
- pjmlp 1y agoWhat Kotlin offers is already present in Scala or other languages from ML linage.
- varispeed 1y agoWith autocomplete being so good today, do we really need languages with cryptic syntax that obscure what is going on?
- Alifatisk 1y agoTo an outsider watching Ruby, it's cryptic, esoteric and maybe magical. But when you actually use it and learn mechanism underneath it, things start to make sense. However, Rubys dynamic architecture have also made it difficult for DX. Things like autocomplete barely works in the ecosystem, because it's so unpredictable until runtime
- hshdhdhehd 1y agoIs a block basically a lambda or is there more to it?
- simonask 1y agoThey are closures. But Ruby can do interesting and slightly reckless things, like transplanting a closure into a different evaluation scope. It’s very powerful, and also very dangerous in the wrong hands.
- hshdhdhehd 1y agoSounds a bit like a lisp macro? Or in JS using eval?
- vidarh 1y agoIt's a closure. The lisp equivalent is also a closure, and the JS equivalent is a function (which is a closure as they can refer to variables in the scope they're defined in. To test this define a function inside a function and reference variables defined in the outer function, and return the inner function).
- vidarh 1y agoI mean, that is the point of a closure. The name refers to closing over its environment. In terms of implementation, the closure needs to keep a reference to its environment, so it may be "transplanted" but it effectively runs in its own scope.
- torben-friis 1y agoComing from a language with functions as first class objects, blocks felt a bit limited to me, because it feels as if you almost have functions but not really, and they get inputted by a back door. Used for example to: let isLarge = a => a>100; numbers.filter(isLarge) Blocks let you do the same but without extracting the body as cleanly. Maybe it’s a chronological issue, where Ruby was born at a time when the above wasn’t commonplace? >When you write 5.times { puts “Hello” }, you don’t think “I’m calling the times method and passing it a block.” You think “I’m doing something 5 times.” I’m of two minds about this. On the one hand, I do agree that aesthetically Ruby looks very clean and pleasing. On the other, I always feel like the mental model I have about a language is usually “dirtied” to improve syntax. The value 5 having a method, and that method being an iterator for its value, is kinda weird in any design sense and doesn’t seem to fix any architectural order you might expect, it’s just there because the “hack” results in pretty text when used. These magical tricks are everywhere in the language with missing_method and the like, and I guess there’s a divide between programmers’ minds when some go “oh that’s nice” and don’t care how the magic is done, and others are naturally irked by the “clever twists”.
- WJW 1y ago> The value 5 having a method, and that method being an iterator for its value, is kinda weird in any design sense and doesn’t seem to fix any architectural order you might expect, it’s just there because the “hack” results in pretty text when used. I don't think this is particularly weird, in Ruby at least. The language follows object orientation to its natural conclusion, which is that everything is an object, always. There is no such thing as "just data" in Ruby, because everything is an object. Even things that would just be an `int` in most other languages are actually objects, and so they have methods. The `times` method exists on the Integer classes because doing something exactly an integer number of times happens a lot in practice.
- torben-friis 1y agoI don’t have an issue with the “everything’s an object” part, because it _is_ consistent, even though it gets a bit trippy when classes are objects as well and they are implementation of a Class class which is an implementation of itself (trickery again!). The issue is more with this part: >The `times` method exists on the Integer classes because doing something exactly an integer number of times happens a lot in practice. It is practical, but it breaks the conceptual model in that it is a hard sell that “times” is a property over the “5” object. The result is cleaner syntax, I know, but there is something in these choices that still feels continually “hacky/irky” to me.
- inopinatus 1y agoFor this audience it may be worth noting that Ruby’s blocks are closures and are passed to methods either anonymously/implicitly or as a named parameter, may be subsequently passed around to any collaborator object, or otherwise deferred/ignored, have the same range of argument arity as methods and lambdas, can even be formed from (and treated similarly to) lambdas, and are thereby fundamental to Ruby’s claim to being a multiparadigm language even as they also betray the Smalltalk roots. In addition they have nonlocal return semantics, somewhat like a simple continuation, making them ideal for inline iteration and folding, which is how most new Rubyists first encounter them, but also occasionally a source of surprise and confusion, most notably if one mistakenly conflates return with result. Ruby does separately have callcc for more precise control over stack unwinding, although it’s a little known feature.
- rubyfan 1y agoThese were some of the best long running sentences I’ve read in a while. A true rubyist!
- rubybaby99 1y ago[flagged]
- inopinatus 1y agoThe hard part is doing without semicolons.
- dragonwriter 1y ago> can even be formed from (and treated similarly to) lambdas They are also used to create lambdas (even the shorthand stabby-lambda syntax desugars to a call to Kernel#lambda with a block.) > Ruby does separately have callcc for more precise control over stack unwinding, although it’s a little known feature. callcc is included in CRuby but has been sidelined from Ruby as a language separate from CRuby as an implementation for a while, with Fibers understood to cover the most important use cases for callcc.
- 1y ago
- teaearlgraycold 1y agoThis level of cuteness and obsession with syntax is partly what drives me away from Ruby. A function should just be a function. We don't need to make programming languages look more like English. There are certainly issues with other languages and their verbosity (like with Java). But I don't want to have more ways to do the same thing and worry about how poetic my code reads as English - we should worry how poetic it reads as computer code. That said, we don't need just one programming language. Perhaps Ruby is easier to learn for those new to programming and we should introduce it to students.
- stonecharioteer 1y agoI think Ruby teaches a sense of style, but I'm not sure that style carries over to other languages. Python was my primary language for 12 years but I'm disappointed in the depth of Knowledge python Devs have. Most barely understand the language or try to. Ruby seems to coax people into coding into a ruby way. I like that.
- teaearlgraycold 1y ago> but I'm disappointed in the depth of Knowledge python Devs have Not super relevant, but my favorite fun fact about Python (specifically CPython) is that hash(x) where x is an int is always the value of the int - except for -1, because the underlying C function uses -1 to represent errors. So hash(-1) == -2, which means you have a hash collision right around 0 where hash(-1) == hash(-2)
- shevy-java 1y agoBlocks yield a lot more flexibility to ruby. It was the primary reason why they are so well-appreciated.
- politelemon 1y agoIt's only more readable if you already understand it. Otherwise it is not, it requires the same kind of hand waving that happened at the start. Important to understand that readability doesn't mean it should be closer to natural language, in programming it means that a junior dev troubleshooting that code later down the line can easily understand what's happening. The python examples are certainly more readable from a maintainability and comprehension standpoint. Verbosity is not a bad thing at all.
- mcphage 1y ago> It's only more readable if you already understand it. Otherwise it is not, it requires the same kind of hand waving that happened at the start. Yes, but it’s a core language feature, so if you spend any time programming Ruby, you’ll come to understand it.
- Alifatisk 1y agoI believe the underlying behaviour of Ruby blocks is one of those mechanics that isn't talked about that much for newcomers, they just get used to how Ruby code look like when they see Rails, Cucumber or RSpec Blocks and method_missing is one of those things in Ruby is what makes it so powerful! I remember watching a talk where someone was able to run JS snippets on pure Ruby just by recreating the syntax. That proves how powerful Ruby is for creating your own DSL It's also a double edged sword and something you have to be careful with on collaborative codebases. Always prefer simplicity and readability over exotic and neat tricks, but I understand the difficulty when you have access to such a powerful tool
- nasmorn 1y agoIMO blocks are not something to be careful about in ruby. If you don’t use blocks you are the weirdo in this language. Method missing is a different beast altogether. I would probably avoid it nowadays.
- vidarh 1y agoThere are legitimate uses of method_missing (though often you'd want to call define_method dynamically instead for performance), but they tend to be when you use an object as a proxy for something (say a remote API) where you genuinely can't know the set of messages the object might need to be able to receive.
- dorianmariecom 1y agoblocks are just procs :)
- Alifatisk 1y agoIt does look like it but procs encapsulates a block, but a block alone is not a proc
- kaiuhl 1y agoThat’s just a performance optimization because blocks are typically only invoked, not passed or otherwise manipulated. If you want to do anything with the block, you pay a tiny tax and have a Proc instance.
- SafeDusk 1y agoI recommend reading Shopify CEO Tobi's try[0] for good example of how Ruby's block behavior and meta-programming makes it easy to create a single file, shell wrapper. [0]: https://github.com/tobi/try/blob/main/try.rb https://github.com/tobi/try/blob/main/try.rb
- codeduck 1y agowhat the fuck...
- itsme0000 1y agoMarket forces will ensure this system isn’t utilized regardless of how good it is. Ruby is simply to similar to Python to consider training people solely in that. It’s a hard reality because I’m sure Ruby is better according to some criteria, but be realistic their share of the market is going to shrink until it’s not really an option of most large companies. I know people will disagree with me, but I wish I knew earlier in my Carr how little this sort of thing matters.
- an0malous 1y agoLet’s check back in 6 months after the AI bubble pops
- itsme0000 1y agoWhy? Python is already 10x the usage of Ruby outside of AI? Find a recent grad who even knows what Ruby is.
- deleted 1y ago[deleted]
- mistercheph 1y agoThankfully, programming languages are subject to strong network effects but not massive ones, Ruby will continue to be a niche language that has active users and projects for many more years than you expect.
- itsme0000 1y agoThat’s going to reassure nobody.
- Dan42 1y agoThis is really cute and heartwarming. Back in the day, a lot of people including me reported feeling more comfortable in Ruby after one week than all their other languages with years of experience, as if Ruby just fits your mind like a glove naturally. I'm glad new people are still having that "Ruby moment"
- nixpulvis 1y agoRuby is still the first tool I reach for if I need to do something quickly and naturally. People are always surprised at how readable the code comes out even compared to python.
- codeduck 1y agoThe three stages of ruby: 1. Someone scoffs at you for writing ruby instead of python / go / javascript / kotlin 2. Then they read the code 3. Then they install ruby It's an amazing language and remains my go-to for anything that doesn't need protocol stuff.
- stonecharioteer 1y agoThank you for reading this. I'm having fun learning Ruby. I just started working at a company where I use it full time. It's great learning it and I have supportive colleagues who are excited for me. I'm going to write more about Ruby. I have planned about 6 articles in the next few weeks. I hope I get around to them all.
- rvitorper 1y agoI can’t unsee it either. Will try it later
- leemcalilly 1y agoWelcome
- thepaulmcbride 1y agoRuby is really let down by the tooling around the language. The language itself would be so much more fun to write if the lsp would reliably jump to the definition of functions etc that seem to appear out of no where. It has been the biggest source of frustration for me while learning Ruby.
- stonecharioteer 1y agoI'm hoping the new age tooling that's coming out is going to make things better. I want to contribute to rv personally.
- Mystery-Machine 1y agoYou're right! Although I get faily far by using Bust-a-gem VS Code extension. (The underlying ripper-tags gem can work with any IDE) https://github.com/gurgeous/bust-a-gem https://github.com/gurgeous/bust-a-gem I have an "on save" hook that runs ripper-tags on every file save. This keeps the definitions always up to date.
- mmcromp 1y agoI say the opposite, the lack of tooling highlights the weakness of the language. The drive to make it declarative/mimic "natural" language by reshuffling and overloading can be "delightful" to some, but beyond the paper covered surface is a mess. And for what ? "5.times do something unless" isn't cute to me. It's a dog "talking" by putting peanut butter in his mouth. But I think I'm the only one who feels this way
- lioeters 1y agoWhat I've noticed about language design is that people confuse their preferences and opinions with genuine objective observations. It's more of an art and aesthetics than a science. I agree "5.times" is an abomination but that's a subjective reaction - there's no principle or law that it violates (other than perhaps principle of least surprise). We can't quantify and measure how good or bad some invented syntax is. As for lack of tooling, it apparently wasn't a priority when the language was designed. I'm guessing the emphasis was more on the ease of reading/writing, and having internal logical consistency. How such language design decisions make the development of tooling more difficult was a secondary concern.
- Mystery-Machine 1y agoRuby is beautiful. It's weird, and different and therefore a bit repulsive (at least to me it was) at first. But, once you learn it, it's so easy to read it and to understand what's going on.* * Side-note: Sometimes variables or methods look the same as parenthesis () are optional. So, yes, there's more things that can look like magic or be interpreted in multiple ways, but more times than not, it helps to understand the code faster, because `clients` and `clients()` (variable or method) doesn't matter if all it does is "get clients" and you just need to assume what's stored/returned from that expression. Also "get clients" can be easily memoized in the method implementation so it gets as close as possible to being an actual variable.
- t-writescode 1y agoRuby Blocks and how they're used literally everywhere is one of the hallmarks of why it's so nice to write the first time in that language; and it shaped how I use and select future languages. Ruby Blocks are almost certainly the reason why I love Kotlin so much - it feels like a well-typed, curly-bracket-styled Ruby in those ways. The collection operation chains in both languages just. feel. good. And I blame Ruby for my first exposure to them, and possibly a lot of people's early exposure to them that helped languages that came after become better. Ruby does take it to a-whole-nother level though, in particular with its 'space as separator' syntax, so you can make a *robust* DSL that's even more powerful than Kotlin's "if the last param is a function, you can just put a curly bracket and go" style.
- user3113 1y agoRuby is my favorite language. There is no other language with same ergonomics. I dont understand hype around ts and all frameworks that changes every week.
- username223 1y agoWas this human-generated? Clearly it's self-submitted, and the useless "hero image" looks like lazy AI slop with Pixar characters, but did a human generate the text?
- stonecharioteer 1y agoI wrote this entirely. I did use Gemini for the post image, I cannot draw and I'm not using my blog to monetize anything so I don't think there's anything wrong in AI generated images.
- bashkiddie 1y agoI heard some rumor that ruby was a type 2 lisp. There were some guys who rate programming languages on how much lisp features they reassemble. What is the source?
- rockyj 1y agoRuby is still so good to read and hack things with. It is a shame that it is not so popular and you know the reasons why. I still wish with such a good DSL friendly structure, it should have become an IaC de-facto standard language or used in some niche where I could use it freely without question.
- rapind 1y agoIt was revolutionary for readability and has influenced a lot of newer languages. That's a pretty good legacy IMO.
- culi 1y agoThe biggest thing holding Ruby back is lack of gradual typing imo. I honestly think javascript is a better fit for IaC. Not only is already the language of the web (everyone has to know some javsacript) but JSDOC is supported by most IDEs giving it gradual typing. Many people don't realize jsdoc is typescript and is a full replacement. Nowadays I like to reach for Julia for quick one-off scripts and webscraping. It has a beautiful and accessible syntax with built-in gradual typing. I would love to see it more widely adopted in the IaC world
- trenchgun 1y ago> The biggest thing holding Ruby back is lack of gradual typing imo. There is: https://sorbet.org/ https://sorbet.org/
- xigoi 1y agoI still don’t get how it "adds two numbers" do calc = Calculator.new expect(calc.add(2, 3)).to eq(5) end is supposed to be more readable than test "add two numbers": let calc = Calculator.new check calc.add(2, 3) == 5 (The latter is Nim with the std/unittest module.)
- Alifatisk 1y agoYour example is Rspec, a testing framework for Ruby, not everyone enjoys it Here's with Minitest (part of std) it "adds two numbers" do calc = Calculator.new assert_equal 5, calc.add(2, 3) end If you want even closer to yours, the following works just fine assert calc.add(2, 3) == 5 Better?
- hambes 1y agoThis is the first time I ever got ruby. Reading it always felt like magic to me and I think it's still too much, if I really want to understand the abstractions, which unfortunately I do. But the understanding of blocks and especially instance_eval has helped a lot.
- stonecharioteer 1y agoI'm glad I helped a bit. I'm going to write a few more posts since I just started a job where I need to do Ruby full time. I'm writing about Loops today.