4 ms·
I love Ruby too. It's a great language, and as a language I like many things about Ruby a lot more than python. In particular, chains of functional operations l
by atomicnumber3 3y ago
I love Ruby too. It's a great language, and as a language I like many things about Ruby a lot more than python. In particular, chains of functional operations like:
arr
.map{|o| ... }
.reject{|o| ...}
.reduce(init_acculm){|init_acculm, o| ...}
are super super clean and expressive. Very similar to what I like about Java streams.
And the library ecosystem is great, I like how it shares spiritual similarities with python where libraries are very "no nonsense" (you don't need extensive configuration and builders and researching a million configuration items etc etc... looking at you, Java) and you typically just import and go. Rails, of course.
I keep picking python over ruby though, for things I'm going to have to actually maintain. And I typically pick Java over python if I smell I'm going to care even an iota about performance. (Often I don't, though). But ruby vs python, I keep coming back to the divergent opinions they've taken on gradual typing. I like that python3 lets you include the types as part of the program, part of the grammar. Ruby relegates them to a separate file. I guess the intent is that it's more for libraries, like how js libs will ship typescript type files? But I don't like that, I want types for myself. Sorbet exists, of course, but I don't like that it's a) a gem and b) still not a first-class part of the grammar but is instead just operating "in-language". I know it works and e.g. Stripe uses it to great effect (I worked there) but I just don't like it, personally, and I find that python3 with its built-in type hinting tends to get typed more readily than ruby where it's a much further reach away.
But I really love ruby. I hope it improves its type-hinting story because I like most other things about it. But I was pretty unenthusiastic about python prior to it getting its type hinting built-in, so apparently this is a big deal for me.
- woodruffw 3y agoBlocks and `yield` are a killer feature; I regularly pick Python over Ruby for similar reasons as you, but I consistently find myself missing those two (and all the things that get built out of them, like the `Iterator` APIs). Python is a lovely language, but context managers often feel like a shabby substitute for what I can do with `yield` in Ruby :-)
- endgame 3y agoThis is basically how I feel about true first-class functions when I have to wrangle ruby and care about blocks, procs, and lambdas.
- MichaelDickens 3y agoI love the syntax of Ruby blocks (IMO it's the best syntax for higher-order functions in any language) but I don't understand why they were implemented the way they are, where some things are blocks, some things are procs, and neither one is really a function. I would prefer if Ruby blocks were just functions.
- zoky 3y agoYou can convert between blocks and Procs very easily. Ruby has several methods to let you do that, both as syntax and as methods. They are really two sides of the same coin; a block can be thought of as a Proc that is intended for immediate usage (though this is convention, and of course nothing prevents you from converting a block into a Proc and stashing it away for later). As for functions, Ruby doesn’t really have them—or, rather, blocks and Procs really are the closest you get to a function. Instead, since everything is an object, Ruby has methods instead of functions. Critically, because methods are defined on objects they have inherent object context, which is why you can’t treat a defined method as an anonymous function. Of course, there are ways to extract the method as a Proc and pass it to another object (with or without its object context). But having methods be objects the way they are in, for example, JavaScript would require extra magic under the hood, and lead to all sorts of context gotchas, which is probably why it doesn’t work that way.
- djur 3y agoIn 15+ years of writing Ruby for production I can't think of a single time where the distinction between blocks and procs caused a problem. Blocks are a syntactic element, procs are a data type (that you can construct with a block). I honestly don't even know where the problem is supposed to come from.
- soulbadguy 3y agoI agree that pipeline functional stream processing are in many case simpler and clear than weird nested loops with early exit conditions. However, besides maybe the needs for lambda expressions, this is mainly a function of good API design as opposed to something code to the language. In particular here i don't think the niceness of this approach is related to anything core to ruby. C# with link, F# with computational expression (and haskel monadic structures) and as you mentioned java stream do the same thing and even a bit better in my opinion
- atomicnumber3 3y agoRuby's blocks as a way to express a HOF is pretty accessible, and I like that I can switch from "arr.map{|o| ... }" to "arr do |o| ... multiple lines ... end" seamlessly, to be terse when I want to be but then be verbose when I need to be. But that's not exclusive to Ruby, Java's lambda syntax behaves similarly. More generally, and more to your point, I think extensive use of blocks/HOFs lends to the "spirit" of Ruby being a lot more functional-style than python, which iirc got lambda support late and they're kind of ugly (I mean, you literally type lambda! almost as bad as go!).
- neonsunset 3y agoYou can do this in C#, F#, Rust and many other languages too with 10x better performance (in Rust in particular those usually get completely optimized away).
- cardanome 3y agoHonestly, I think both Python and Ruby have a subpar gradual typing story. The problem is that Python type hints are not enforced at runtime. They can can not be trusted. You have to rely on external type checkers that are relatively slow and different checker might give you different results. Probably the only mainstream backend-language getting gradual typing right is PHP. Yes, good old PHP. Dead simple type system, no productivity-sucking compile step like in Typescript, actual runtime enforcement of types contrary to Python and great static linting. Though PHP is also a fair bit less expressive and dynamic compared to both Python and Ruby so it can get away with an much simpler type system. Then again, as someone who has to work with many legacy systems, sometimes I am very grateful for the lower expressiveness.
- atomicnumber3 3y agoY'know it's weird but it's never bothered me too much that python type hints don't do anything at runtime. It's enough to just know what someone, at some point, intended. (And PyCharm is pretty good about pointing out when they're wrong, anyway). I guess when it comes down to it, given we're not statically typing, I still get an awful lot of mileage out of just knowing when my assumptions about the code conflict with someone else's assumptions about the code (even if that other person is subtly wrong, like anystr vs str or something). Sorbet has a runtime time-checking mode I think, too, so that's actually a point in favor to ruby for you (though again with the drawbacks I mentioned of being a gem and imo being a little clunky syntax-wise).
- Spivak 3y ago> Ruby have a subpar gradual typing story Whether you agree with it or not Ruby's (the language not the developers) resistance to static typing is just how Ruby wants to be written. There are no types, only messages. The whole culture surrounding Ruby is burying all the underlying data in favor of pure message-passing nirvana. I have no ducks but I must quack.
- djur 3y agoThis is partially true, but a pretty large chunk of idiomatic Ruby is focused on Enumerable method chaining, transforming arrays and hashes, slicing and dicing strings, etc. A language that was totally dedicated to message-passing nirvana wouldn't have regexes as a built-in data type with a literal syntax (or dictionaries, ranges, and so on)
- dbalatero 3y agoI also worked at Stripe, and loved Sorbet-ified (typed) Ruby, but any time I'd recommend it to a friend it would fail to work in their project out of the box. I think it works really well on Stripe's codebase, and might work on another one if you have what it takes to wrestle with it. And aren't ahead of Stripe on Ruby versions…
- ecshafer 3y agoSorbet works well on Shopify's codebase. We are basically always on the new version of Ruby and Rails both. Sorbet wasn't a huge effort to get working on the code I work on, but its been mostly easy keeping going.
- grogenaut 3y agoquestion, first glance sorbet looks like an awkward version of typing syntax wise that makes ruby look like other languages. At that point what still stands out about ruby that makes it still great with a lot of type overhead? I did ruby / rails for years and enjoyed int. Just wondering.
- ecshafer 3y agoI am not a fan of sorbet. I just know its been relatively painless. I haven't really had any extra confidence refactoring from Sorbet, nor have I noticed much beyond it being another step in making code. Mostly I think its kind of an ugly solution, and I would like something else for optional typing.
- dbalatero 3y agoMy friend's experiences ranged from "too many papercuts to deal with, gave up" to "broken entirely on Ruby 3", but good to hear it works for you. For the Shopify case, do you not have a Ruby infra team that manages the install?
- ecshafer 3y agoYeah there are teams for ruby and rails infra, so my pretty smooth experience might just be that team doing the work to hide the issues from the rest of the developers.
- usefulcat 3y agoI also love Ruby. I've been using it for over 20 years now, although always in a 'supporting' role--like at most maybe 5-10% of the code I write, and I definitely don't read or write it every day. I've also never written anything particularly large in it. Like around a few thousand LOC, tops, for any one tool. And at that point, I often find myself writing a lot of things like assert { foo.is_a?(Foo) } to check parameter types, because beyond a certain size I just can't keep all the details in my head, and I don't want to rely only on comments. Guess I'm saying that even though I really love it for small things, I'm also skeptical of its ability to scale.
- amomchilov 3y agoThe special-casing of Blocks irks me, including the fact that you can only have 1 of them in a single method call. I understand the justification, but it’s syntactically inconsistent and doesn’t sit right with me. I really like the way Swift introduced closures as regular parameters, but lets you have the Ruby-like “trailing closure syntax” for the last one in the list. It’s very best-of-both worldsy: if you need just one closure, it can be trailing, and if you need multiple, they can both look the same.