A lot of people seem to overcomplicate it by bringing their thoughts on how other modern js apps are written. It would be helpful to have a specific example of some of the things you need to work through. Reading through the [handbook](https://hotwired.dev/) should get you most of the way there.
One of my colleagues recently switched from mostly react to default full stack Rails. He was really struggling with how to filter things in a table. When I showed him how I would do it, his comment was along the lines of: “I can’t believe how simple this is. Modern JS just doesn’t work this simple any more. This is like how jquery used to work but way more organized”.
I can’t say I know enough about modern js development to validate that comment. He noted something about expectations of how the dom managing things…
So, if you’re coming with a certain mindset of how to do things, try to leave that behind a moment and read through the handbook. Especially if it involves making fetch requests to do things. That’s like, the number one “you probably shouldn’t do it like that” in Hotwire.
Some of the most enlightening books I’ve read when I was first learning Ruby were Text Processing in Ruby, and Building Awesome Command Line Apps in Ruby 2. They each reveal certain features and perspectives that work towards this end, such as text parsing moves, Ruby flags to help you build shell 1-liners you can pipe against, and features with stdio beyond just printing to stdout.
Then add in something like Pry or Irb, where you are able to build castles in your sandbox.
Most of my data exploration happens in Pry.
A final book I’ll toss out is Data Science at the Command Line, in particular the first 40 or so pages. They highlight the amount of tooling that exists that’s just python shell scripts posing as bins. (Ruby of course has every bit of the same potential.) I had always been aware of this, but I found the way it was presented to be very inspirational, and largely transformed how I work with data.
A good practical example I use regularly is: I have a project set up that keeps connection strings for ten or so SQL Server DBs that I regularly interact with. I have constants defined to expedite connections. The [Sequel library](https://sequel.jeremyevans.net/) is absolutely delightful to use. I have a `bin/console` file that sets up a pry session hooking up the default environment and tools I like to work with. Now it’s very easy to find tables with certain names, schemas, containing certain data, certain sprocs, mass update definitions across our entire system.
```
# Something failed, and not everything loaded as you expected
# explore explore explore…
Just like with work in the shell, you have a really easy time iteratively exploring the problem and building up your answer. The ability to serialize your data you’ve found, and keep your favorite tools in your pocket feels extremely productive. And of course, all of this can be written in ruby 1-liner shell scripts, or more complex shell scripts to pipe in and out of other tools if desired.
It is pretty cool. I’ve been using it for 3-4 years as a queue between Ruby and Node for scraping tasks. Ruby queues the work, a node worker does the downloading, and a Ruby process parses and loads data. It works incredibly well in my use case and has saved me from having to shift everything to one language or the other. It’s been very reliable.
I usually drink the DHH koolaide when it comes to Rails decisions, but I'm surprised by this decision exactly because of his arguments against being forced to write specific styles. I enjoyed default RuboCop when I was first learning Ruby to gain idiomatic knowledge, and strongly disliked after I took off my training wheels. I don't even like using `standard-rb`, which is a much more tolerant configuration of Rubocop. There are often reasons to disregard certain style rules, and I don't want to ask for permission in `# magic disable comments`.
I strongly agree with all of his descriptions of formatting and linting. I also strongly agree with him putting his personal style into Rubocop for the Rails codebase itself, and for project maintainers to be in control of what, if any, style they wish to enforce. But to omakase his style-guide into the consuming projects is surprising and against the very arguments against conforming to style-guides he presents in the first half of the post.
I am in the camp of using as little rubocop as possible, but still using it. I like it to address the drastic stuff like misalignments, mixed tabs and spaces, obvious sloppiness.[1] Still, it should allow most of the freedom. My preferences are posted in what I call a "narrative" Rails template[2]. It's a relaxed "standardrb", which I have since relaxed even further, but haven't updated the repo yet.
I'm fluent in Ruby, and have nearly zero experience in the other languages featured in this article. The `implicit return` is often something people find rather willy-nilly when first learning Ruby. As such, I always thought that was a feature explicit to Ruby and its peers (historical influences like Lisp, Smalltalk, and futures like Elixir and Crystal).
I am surprised and delighted to learn that in fact, many modern languages have been doing this. Particularly seeing this in Rust raised my eyebrows, given how system-focused it is. I'm curious from the Rustaceans out there: Is it common to use the conversational nature of expressions and implicit returns? Or does it frequently create unwelcome dissonance?
Whenever I switch to Python, JS, C#, the explicit returns catch me for a day or two. I always think "We're already reserving this value in memory to evaluate that last line, why don't you let me do something with it if I want?". But I know folks who swing the other direction and feel quite the opposite -- things feel out of place and not-obvious.
Either way, thanks for the well-written article. It has added influence to my "I think I want to look at Rust -- it feels familiar."
GNU C did it decades ago (but of course the GNU project is laced with Lisp influence).
In GNU C, if a braced compound statement is put into parentheses, it becomes an expression. The value of the last expression in the compound is implicitly returned:
// c receives 5
int c = ({ int a = 2, b = 3; a + b; })
Never mind that; the standard C macro preprocessor has "implicit return":
Note that the "implicit return" in Lisp is just from mathematics:
f(x, y) = x^2 - y^2
Not:
f(x, y) { return x^2 - y^2 }
the return keyword is kind of a disease/blemish, introduced by way of Blub programming languages.
"Implicit return" is like saying "face with implicit warts" in reference to a face that is perfectly smooth and never had a blemish.
In functional languages, there is only one expression which makes up the body of a function and of course that expression's value is yielded; it's the definition of the function.
Explicit return is a form of goto; it's required only in imperative programming, and even there we can leave it out for the last statement.
The C preprocessor doesn't have a return operator because it's a functional, term rewriting language. Every definition has one replacement in which there are no side effects or goto.
The problem is that "implicit return" is the wrong mental framing. Expressions evaluate to a value. That's true in every language that supports expressions. Some just go more all-in on expressions than others do. This is important because
> Is it common to use the conversational nature of expressions and implicit returns?
I have found that most people who do not have experience in expression-oriented languages end up confusing themselves because of it. They'll ask how you're supposed to know where a function returns, when a return can just happen anywhere. But it can't! That's not how it works!
Like any new concept, it can take some time to wrap your head around how it influences how you write code. But I think that the end result is far better. Compare this lambda in C++:
[](int n, int x) { return n < x; }
to this closure in Rust:
|n| { n < x }
there are multiple reasons why the Rust is shorter here, and you can argue some of them are better or worse, but not needing the return and semicolon here makes it easier to read, at least for me.
But I think that sometimes people who learn about "implicit returns" write things like iterator chains, that take closures, not even realizing that they're using "implicit returns" in that circumstance. It just feels natural, especially in simple cases like this.
But also, I've been mainly writing in expression-oriented languages for the majority of my professional career, so of course it makes sense to me.
I think that's a great argument to consider when framing this concept to someone the first time. Avoid the `implicit return` language altogether. Instead focus on the expressions. `Implicit` triggers mental responses like "I prefer explicit over implicit" but you're right, that's just a side effect of what's really going on here.
Thank you, and yeah, I didn't even think about that aspect of it too, but you're right. I think the reason it's a popular formulation of the semantic is that it's kind of like, trying to describe it from a statement-oriented viewpoint, and there's certainly value in trying to help bridge an understanding gap by relating it to things that you know. But I worry that it either gives the wrong impression (heck I would hate true "implicit returns" too!) or that people tend to stop there as opposed to truly making the mental shift. This stuff is hard!
I can appreciate that. There's a few revelations I've had over the years of learning Ruby that highlighted "why things are as they are". Decisions that feel arbitrary and against the grain, but actually make a lot of sense when you understand the design as a whole. And I don't mean to imply "You just need to understand the roots, man!". It's probably the reason people who like Ruby are zealous about it, and many aren't. It's all about that Ruby brain glove.
Two great examples that everyone comes into right away are "Why are parenthesis optional? Why do you not have to `return`? Why do you allow such chaos to run abound willy nilly?!".
It isn't because "We just like being different!" or "We are allergic to parenthesis!". It turns out that one of the fundamental designs of the language makes it such that they don't really _mean_ anything.
I think being able to understand some of that context is really valuable to "ruby making sense" vs not. You often see people proclaim "In Ruby, everything is an object!" And it doesn't make much sense why that's a big deal. But the ramifications are actually amazing: Everything (basically) is an object -- even stuff that feels deep in the guts of the core of the language (defining a class for example). It turns out, this creates an amazing amount of _consistency_ where every single thing in Ruby from my app code, down to the low level parts of the language follow the same rules. This then makes it easy to anticipate how something works, or find out more about it.
I don't expect to single handedly fit your Ruby gloves to your brain, but here are some notes that define Ruby that I find enlightening:
- (Pretty much) Everything is an object. A `thing` is an object. A literal `1` is an object. A `class Mom; ... ; end` is an object. They all follow the same rules. If you want to learn what you can do with an object, you just need to learn what kind of object it is, and then you can find out what it knows. There aren't top-level functions to act on your objects. Ruby is all about "passing messages to objects". Your object knows how to handle itself.
- Things that look like operators are just methods on those objects. `thing > other` is calling the `.>(other)` method on `thing`. Accessing data from an object always happens via a method. There's no such thing as "accessing a property vs calling a method". It's all calling a method, always. This adds to the consistency.
- (Pretty much) Everything is an expression. A value is already created and stored in memory somewhere; might as well make it available to the next fellow. This enhances the ability to ... _express_ (sorry) things. You can expect to be able to chain things together, or leverage values as they are created.
- The whole motif surrounds having a conversation with your objects. It's less about making statements to your computer, and more about having a conversation with the context you are working in.
Honestly, these are all common bullet points that people bring up, but to me they didn't make much sense until I really held a strong grasp of the language as a whole. At some point, those bullet points became my "woah" moment of _why_ Ruby is how it is. This understanding in how Ruby was different from other languages I've used helped me understand why Ruby fits my brain like a glove. This style of creating makes a lot of sense to me.
I usually conditionally include the visibility toggle class from the erb side, so the first time it renders as html it has the toggle correctly initiated. I can see situations where the visibility logic is hard or impossible from the Ruby side, but I haven’t had this issue yet.
Unfortunately I haven't come up with a good solution either beyond hiding the element with CSS, and then showing it when the controller connects (which isn't ideal). I'd love any suggestions though for different techniques
In addition to the Ruby under a Microscope (linked elsewhere), a lot of talks from Aaron Patterson (Tenderlove) are enlightening in this regard. I've learned a lot from them, and they also provided the inspiration to keep digging. Some highlights:
I would suggest that the Rails scene is at least worth checking out.
I’ve seen a number of people move from .NET or Node to Ruby and Rails and thoroughly enjoy it, with little desire to go back. For reference, the last 12 years I’ve worked for a company that was traditionally .NET. In the past few years, we’ve been choosing Rails instead. Once you know its conventions, you can really fly. It’s very mature and doesn’t often change in frustrating ways. What I learned in 2014 is still very relevant today.
Especially on small teams or solo, it’s amazingly productive. The out-of-the-box experience gives your users the feeling of a modern SPA without actually being one. Instead, it’s built like a .NET MVC app with standard resource rest routes and Razor views. Significantly less boilerplate. Hard to describe, but very clever. It makes me feel powerful, and I really enjoy programming with Ruby.
But maybe it’s not what you’re looking for if you want a single final frontier to ride off to. Some people really dislike Ruby’s dynamic nature, and Rails’ mysterious-seeming clever conventions. (Though, note, it’s not magic, it’s just clever Ruby code. Everything can be traced and explained). Other languages have a larger market share and perhaps more excitement. But, your described use-case of solo web-dev moots that point.
Consider looking through https://rubyonrails.org/ to sniff it out. Watch that video to see if it jives.
https://gorails.com/guides is a great resource to learn how to set up a dev environment, with tons great modern content.
Whatever you choose, good luck. I hope you find a great fit!
Thanks for the thoughtful feedback. I have a feeling I'd probably find a little joy again by using Ruby/Rails. The only thing that gives me pause is performance. From what I recall, anyway, but maybe it's improved since a investigated a few years ago. Will give it a shot, thanks!
One of my colleagues recently switched from mostly react to default full stack Rails. He was really struggling with how to filter things in a table. When I showed him how I would do it, his comment was along the lines of: “I can’t believe how simple this is. Modern JS just doesn’t work this simple any more. This is like how jquery used to work but way more organized”.
I can’t say I know enough about modern js development to validate that comment. He noted something about expectations of how the dom managing things…
So, if you’re coming with a certain mindset of how to do things, try to leave that behind a moment and read through the handbook. Especially if it involves making fetch requests to do things. That’s like, the number one “you probably shouldn’t do it like that” in Hotwire.