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.
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.
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.
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 class Integer
def times(&blk)
i = 0
while i < self
blk.call(i)
i += 1
end
end
endNo, 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.
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!!)
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).