I have a few minutes to spare right now, so gonna try setting up my dev environment now.
#jeweljourney https://twitter.com/lushorac/status/1278038216696872964
My IDE of choice has been IntelliJ for quite some time now, and since there's a dedicated Ruby Version of it, the plugin for it should work great, right? We'll see!
So I'm seeing a few plugins that might seem useful, but I'll stick with just the base Ruby plugin for IntelliJ for now and see how far that gets me.
Plugin installation: Check. Let's create a new project.
Heh, forgot to install Ruby first. Well let's do that then 😅
I'm on Windows, and while Chocolatey is great, I think I'll stick with the default installer for now to not complicate things further
So the stable version is 2.7.1, but the ruby installer page recommends 2.6.X for best gem compatibility? Alright, going with that then
Okay... I have no idea what I should choose here
The help page on RubyInstaller doesn't help, and a quick google also didn't turn up anything useful. Just going with the base installation then and see if it works 🤷‍♀️
Oh lol, directly after starting that I read the "If unsure press ENTER" bit. Whoops. Learning the hard way, I guess.
Okay great, it seems to have worked, interactive ruby is running properly! Then let's proceed in IntelliJ
Project creation done, I have an empty ruby project 🎉 So... What to do now? I have no idea if there is a standard structure for ruby projects, IntelliJ at least hasn't created one. Well, I guess I'll just create a few random files for tinkering.
Once we get to Rails I guess there will be a structure I can use.
That worked, awesome! Eager to test out IDE support now, but having a meeting in a few minutes, so I'll pick this up again later.
Alright, meetings over, some more work done, let's get back to this for a little!
Alright, a little playing around in the editor, I've built a small AI that definitely passes the Turing Test!

Kidding aside, this went quite well. Already stumbled across a few more language features just by goofing around with auto completion. Good start!
I also accidentally started the script in debug mode at one point, but couldn't quite figure out how to use that - let's see if I can figure out how that works now.
Oh okay so it actually does work as I expected, but my script expects some input before doing anything. And it's not obvious how to put something into stdin from the debugger console
Yeah, this isn't working. I'll skip it for now - directly typing into stdin is not something I'll be using going forward, so fine by me.
So, I figured I should install the rubocop gem to have a consistent style enforced from the start. However, none of the Ruby cli tools seem to be working in my command prompt - instead, I have a dedicated "Start Command Prompt with Ruby" entry in the start menu. That's... Weird?
The Ruby binaries are on my path, and I have restarted my shell... Well, let's just restart the PC for good measure. It's Windows, after all 🙃
That seems to have done the trick!
Turns out I actually don't need the shell to install rubocop because IntelliJ can do it for me. Nice.
...aaaand all my code is yellow now. Amazing! 😂
One of the inspections has set me on the path to see what "Frozen String Literals" are now.
This syntax is crazy
Apparently, this is just the syntax for multiline strings, and there are multiple, cryptic variants with different treatments in respect to newlines and indentation. Hoookay.
So, objects in Ruby can be frozen, i.e. made immutable. Including strings.

...does that mean that strings are not immutable by default? 😳
Yes, it does. Oh god.
Anyways, on with the frozen string literals thing
Ohh, okay, so frozen string literals is an option to make all string literals frozen by default - whew. I was already fearing I'd have to manually do that everywhere.
Getting used to the language by doing a few typical puzzles - starting with the fibonacci sequence
Oh jeez, I already see that the muscle memory of entering an opening brace after writing a function signature is going to become a problem 😅
I went down a rabbit hole of seeing if I can do a modulo check in a case statement, and on the other side found out the answer to my earlier confusion about lambdas/blocks ( https://twitter.com/lushorac/status/1280473237592379395) Three different syntaxes, one for multiline, one for inline, and one for >
> actually treating the lambda as an object I can pass around. I guess this has grown historically, but I can't help but think that having just one syntax for anonymous functions/lambdas would be much better...
Oh and then there is this thing called 'Proc', which seems to be what Kotlin does via non-local returns in inlined lambdas, or at least that's what I'm understanding
So... I got FizzBuzz working. I went into the deep end there, giving myself the full load of theory on blocks, lambdas, procs etc to get the case statement look freaking clean!
One step further - using a generic, curried lambda. I don't really understand why I have to use square brackets instead of parens though
So this is the equivalent Kotlin code, as close as you can get without resorting to hackier stuff. Personally I like the Kotlin version slightly more, mostly because of graphical characters instead of keywords, but hey, not much difference overall.
Obviously, I had to try doing the "hackier" stuff. Just for the kicks. So the Ruby `case` expression is clever - for a `when x then ...` branch, it executes `x === y`, where `y` is the subject of the whole case expression. If the result is true, the branch is picked.
Since `x` can implement `===` however it wants, implementing arbitrary branch logic is trivial. Lambdas happen to implement the `===` method in a way that makes `lambda === p` equivalent to `lambda(p)`, so that makes it even easier.
In a Kotlin `switch`, it works *almost* the same. There are two key differences though:
1. The function used is actually the normal `.equals` function
2. Receiver and argument for the `equals` call are switched. Ruby evaluates `b === s`, Kotlin instead does `s.equals(b)`.
This means that in Kotlin a) the subject of the switch expression has to hold all the switching logic, it can't directly be provided by the individual branches, and b) the subject has to be a custom class, because we have to override the `equals` function in a way that makes >
> no sense at all in *any* other circumstances outside this custom switch logic.
Knowing these conditions, we can produce working code that looks like this. Not too bad, I'd say!
What's going on here? Before passing it to the switch, we wrap `i` into a special `DivisionTest` object that overrides the `equals` function to check if the wrapped value is divisible by a number passed to it.
Passing the `DivisionTest` object as the subject into the switch allows us to use plain integers as the branch conditions, which tricks the switch to check for integer division instead of equality.
By the way, notice that our `divisibleBy` extension property is declared as returning `Any` instead of `DivisionTest`? That's because otherwise, the compiler will yell at us, saying that comparing a `DivisionTest` object for equality with numbers makes no sense 🙃
So far, so good! But we can generalize this further.
Remember what I said about the subject having to contain all the switching logic? Well, we can work around that.
First, this is how the finished code looks like. Now that is *really* close to Ruby version, and would absolutely prefer that to the Ruby version, aesthetics-wise.
After the first version, the jump to this one isn't actually that big anymore. Have a look at the magic behind the scenes:
What do we have here? There is a `_is` extension property, that wraps any object we call it on into a `CustomSwitch` object. That object has its `equals` function overridden, so that it works just like normal, *except* if it's passed a `CustomSwitchCondition`.
In that case, it simply delegates to the custom condition, passing the subject to its `check` function, which is just a predicate that can be implemented any way we want - in the case of our `divisibleBy` from earlier, checking for divisibility.
Whoops - had an error there, this is the correct behind the scenes code:
And you know what's awesome? With this implementation, since everything that *isn't* a `CustomSwitchCondition` gets passed through as-is, the switch still works exactly like we're used to for all usual values:
Okay, so I initially started this just to see how far I would get implementing custom switch conditions with the power of Ruby's case expressions in Kotlin, expecting to maybe get something half-usable, but ultimately dismiss it as an experiment, and stress that nobody >
> should actually do this. But after seeing how clean that result looks, I'm actually not that convinced that it would be that bad. I might make a blog post out of this hack and see what others have to say 😅
Okay, I didn't intend for this thread to go on that long, but this case vs. switch challenge really gripped me there. Really have to get to bed now 😴 See you on the next thread!
You can follow @lushorac.
Tip: mention @twtextapp on a Twitter thread with the keyword “unroll” to get a link to it.

Latest Threads Unrolled: