4 ms·
I have been doing Ruby for so long that it feels very natural to apply a method in this way on the instance. false.not applies the not method on the false ins
by oezi 1y ago
I have been doing Ruby for so long that it feels very natural to apply a method in this way on the instance.
false.not
applies the not method on the false instance in the same way that
car.start
in every OO language calls the start method on car as the receiver.
So filter(list) feels just wrong when you are clearly filtering the list itself.
- nasmorn 1y agoAlthough I prefer Elixir currently I agree that ruby at least goes all the way in on OO and not having to remember which feature is implemented as a language syntax and what is just a method invocation is a strength not a weakness. It is different in other languages for historical performance reasons really.
- torben-friis 1y agolist.filter is ok! Filtering is an action that applies to a list false.not is borderline but if read as false.negate it makes sense (negating is an action that applies to a Boolean value). That wording screws the chaining though. 5.times is where the pattern breaks: times is not an action that applies to a number (nor an action at all). It’s the block the one that should repeat/iterate - but Ruby breaks the rule there and blocks are not an object (!). If they were you could block.repeat(5) which IMO is cleaner.
- chao- 1y agoThere is a bit of personal preference in what "applies to a number", but I see what you mean. As a slight correction, a block is indeed an object! They are received by methods as an instance of the Proc class: def inspects_block(&block) puts block puts block.class end inspects_block { "foo" } # => #<Proc:0x0000000000000000> # => Proc You can even add a 'repeat' method to these in the way that you specified, although you will need to add '->' to declare the block (as a lambda, which is also just an instance of Proc) before you call #repeat on it: class Proc def repeat(n) n.times { self.call } end end ->{ puts("foo") }.repeat(3) # => foo # => foo # => foo
- kaiuhl 1y agoBlocks are actually instances of the Proc class. There are helper methods to handle blocks passed to methods in a lightweight manner but you can also accept the block as a method argument, e.g., class Integer def times(&blk) i = 0 while i < self blk.call(i) i += 1 end end end
- dragonwriter 1y ago> Blocks are actually instances of the Proc class. No, they aren't. Blocks can be reified into instances of the Proc class, but they are not objects and reifying them into objects has overhead. Using a & argument asks for a block passed to the method to be reified into a proc accessible in the body of the method, which is useful if you are going to do something with it that you can't do with a bare block.
- kevinmchugh 1y agoFun fact - there's no Boolean class in Ruby. True is an instance of TrueClass and false is an instance of FalseClass
- isr 1y agoHmm, but that's not really a "breakage" in ruby, it's more an aesthetic argument over which objects should have which logic. It's like naming things. Smalltalk (& ruby) has 'select'. Everyone else uses 'filter'. To some, 5.times seems very readable & logical. It's like arguing over the "right" colour scheme to use while coding (BTW, the correct answer is solarised light, but with black foreground text!!)
- hakunin 1y agoI think I feel you. However, I think you have conceptually loaded "method" with more meaning. I think a less loaded way to think of the object/method is that the object is the first argument to the method that is called on it. So 5.times { puts 'hi' } is equivalent to times(5) { puts 'hi' } which you could expand to my_function = -> { puts 'hi' } repeat_times(5, &my_function) And here is another reason for the disconnect: in a purely functional language repeating a function five times is useless, you're doing something for side effects only. Looping in itself is kind of a wrong (i.e. incomplete) abstraction for functional dev, because you're usually thinking in higher level concepts, such as `reduce`-based transformations. Maybe that's another part of the reason why `5.times { … }` feels off. After my foray into functional programming, I actually ended up appreciating Ruby more, because it lets you have it both ways: program your computer directly, and harness functional concepts. Since computer hardware is not functional I don't want the extra ceremony and abstraction over it for the sake of purity. All that said, going back and forth between Ruby and Elixir really conceptually crystallized for me that the method call receiver is basically just the first argument to the method, accessible with the keyword `self` (which in Python is made explicit for example).