4 ms·
Disagree 1000%. Just because you can write a monstrosity in Ruby doesn’t make the language itself inelegant. Ruby, of the many languages I’ve used, has by far
by stouset 9y ago
Disagree 1000%.
Just because you can write a monstrosity in Ruby doesn’t make the language itself inelegant.
Ruby, of the many languages I’ve used, has by far the highest potential for code being poetry. You can find solutions that are incredibly simple and understandable due to so much boilerplate being remove from the syntax. Likewise you can write code that’s impressively concise without being inscrutable — I’m continually astonished by how readable Ruby can be.
One exercise in particular sticks out in my mind. Many years back, I implemented the first few dozen Project Euler problems in Ruby. However, I imposed a limitation: each solution had to be under 72 characters (library inclusions are fine), a single logical line, and readable. The end result was surprising at how naturally it ended up working — the solutions ended up being little more than the high-level conceptual approach, translated literally to code. Solutions ended up looking like the following:
(2 ** 1000).digits.sum
(2..10_000).amicable_numbers.sum
1901.to_year.upto(2001.to_year - 1).select {|d| d.mday + d.wday == 1 }.count
Sure, these depended upon injecting new methods into core classes, but who cares? For this problem it led toward incredibly straightforward and readable solutions for these problems. If this were library code I wouldn’t dream of doing it this way, but for application code — why not?
I have yet to see any other language with Ruby’s capacity for readable conciseness.
- AaronFriel 9y agoAre you familiar with Rust, Haskell, C# or JavaScript? All of these allow creating new extension methods of existing types, albeit by three different methods. I'm assuming that your "2001.to_year - 1" is "the date time for 2001 minus 1 day" rust (traits) (pow::<BigInt>(2u8, 1000).digits().sum() (2..10_000).amicable_numbers().sum() 1901.to_year().upto(2001.to_year() - 1.to_day()).days().iter().filter(|d| d.mday + d.wday == 1).count() Haskell (type classes) sum . digits $ 2 ^ 1000 sum $ amicable_numbers [2..10000] count $ filter (\d -> mday d + wday d == 1) [year 1901 .. year 2000 - day 1] C# (extension methods) BigInteger.Pow(2, 1000).digits().sum() Enumerable.range(2,10000).amicable_numbers().sum() 1901.ToYear().UpTo(2001.ToYear() - 1.ToDay()).Days().Where(d => d.mday + d.wday == 1).Count() JavaScript (changing prototypes) bigInt(2).pow(1000).digits().sum() _.range(2, 10000).amicable_numbers().sum() 1901.to_year().upto(2001.to_year().minus(1)).days().filter(d => d.mday + d.wday === 1).length()
- stouset 9y agoYes, I am familiar with all of them. :) Rust most of all, and that's another language I'm extremely happy with. The others to lesser extents, and Javascript is the only one of them where I'd say I dislike the language.
- dmitriid 9y ago> Sure, these depended upon injecting new methods into core classes, but who cares? I, as a developer reading your code 6 months later, care. When something fails, it's nearly impossible which of the magic methods were injected by which magic library.
- nfm 9y agoNot really. It's hard for your editor to do this statically, but there's heaps of good tooling for finding the method source and docs dynamically. pry> show-source SomeClass.class_method pry> show-source AnotherClass#instance_method # List an arbitrary object's class methods, instance methods, methods mixed in by included modules pry> cd some_object pry> ls The runtime knows how to execute your program (it's not random) so everything you want to know is available.
- dmitriid 9y agoYou realise you just validated my point, right? :)
- stouset 9y agoI'm not sure you did. People who don't use Ruby seem to think this is a huge thing that you run into constantly. It's not. People are for the most part pretty responsible with these things.
- JohnBooty 9y agoBeen doing Ruby full-time for 3+ years, bunch of experience before that. there's heaps of good tooling for finding the method source and docs dynamically I know, but this is a huge problem with a lot of code I see in the wild because this stuff is (ab)used so widely in Ruby-land. If I have to run code just to see where `Foo.bar` is defined, that makes things... about an order of magnitude harder to understand. I mean, that's really tough thing to do in anything but a trivial codebase. This is nearly 100% the fault of developers, not the language itself, but I would suspect that most Ruby developers spend a significant amount of time dealing with things like this because it's how the ecosystem rolls.