Call * method on implicit receiver - ruby

I want to create the next DSL part:
* 'Ruby'
The next experiment shows predictable results:
def *(a)
a
end
* 'Ruby' #=> SyntaxError: (irb):4: syntax error, unexpected '\n', expecting :: or '[' or '.'
self.*(1) #=> NoMethodError: private method `*' called for main:Object
self.send(:*, 1) #=> 'Ruby'
I suppose Ruby syntax analyzer considers such expression as a splat operator or syntax sugar for the asterisk method. Can someone explain this more detailed and (if it's possible) provide solution to resolve my problem?

That's not possible. There are some methods that simply cannot be called without an explicit receiver because they would otherwise be ambiguous. In particular, in every case where you have both a unary prefix and a binary infix operator, it would be impossible to distinguish the two cases:
+ a == a.+#()
- a == a.-#()
& a # interpreted as the unary prefix proc-block conversion operator
* a # interpreted as the unary prefix splat operator
or in cases where the operator conflicts with other syntax:
/ a # interpreted as the start of a Regexp literal
For consistency, Ruby forces the use of two operands for all binary infix operators, not just the ones for which the grammar would be ambiguous.
Likewise, [] cannot be called without an explicit receiver because it would conflict with literal syntax for arrays.
The other well-known example of methods that cannot be called without an explicit receiver, is setters:
self.foo = bar # setter
foo = bar # local variable
For private setters, there is actually an exception to the rule for private methods, that allows them to be called with an explicit receiver, as long as that explicit receiver is the literal pseudo-variable keyword self. However, no such exception exists for calling private binary operators or methods like []. (There are proposals to that effect, though.)

Jörg W Mittag answers why your first expression * 'Ruby' (without an explicit receiver) raises an error.
I will add why your second expression self.*(1) (with an explicit receiver) raises an error. This is because the method is defined on the main object, which are all private by default.
This gives you that the only way to call the method is neither with nor without an explicit receiver, in other words, you cannot call that method with a form of an ordinary method call. You can only pass it in the form of a symbol describing that method, and let another method (send) execute it instead.

Related

How do I use `:rand` as a unary method with map/collect?

I want a vector containing the result of rolling 10 dice (say). I can do this:
([6]*10).map{|x|rand(x)}
But this gives me a "wrong number of arguments" error:
([6]*10).map(:rand)
Is there a point-free way to pass the one-argument version of rand to map?
This will work:
([6]*10).map(&method(:rand))
This won't:
([6]*10).map(&:rand)
Symbol#to_proc is (roughly) implemented like this:
class Symbol
def to_proc
-> *args { args.first.public_send(self, *args.drop(1)) }
end
end
In other words, it takes the first argument as the receiver and the other arguments as arguments to the method that is named by the Symbol. So, in your case, it is calling
6.rand
over and over.
This "kind of" works, because 6 is an Integer, Integer is an indirect subclass of Object, which mixes in Kernel, which defines rand. But! rand is private, which means it can only be called without an explicit receiver. Also, you aren't passing any arguments, therefore even if it were public, you'd get the behavior of rand without arguments, which is a Float between 0 and 1.
Whereas Method#to_proc passes all arguments to the method (it already has its receiver bound, after all), so in this case, it will call
self.rand(6)
over and over. Which of course works, because self is an Object, Object includes Kernel, and rand is defined in Kernel.
This won't work as well:
([6]*10).map(:rand)
For the simple reason that map doesn't have any parameters, but you are passing an argument.
You can write:
([6]*10).map(&method(:rand))
This would be clean way to write it:
10.times.map { rand(6) }
Writing it this way makes it easier to see what argument is passed to rand.

Ruby required keyword arguments

For Ruby methods, the required keyword syntax is nice
def foo(bar:, baz:)
:
end
foo(bar: true, baz: false) # OK
foo(bar: true) # missing keyword argument error
Can one 'splat' the list of required keywords with some kind of magic? i.e.,
required_keywords = [:bar, :baz]
def foo(magic(required_keywords))
:
end
I expect not, but I'm often surprised with what Ruby can be persuaded to do.
The splat (*) and double splat (**) operators allow a method to take an arbitrary number of arguments. The former will store the arguments in an array and the latter will store them in a hash.
There is also the options hash, which is an optional hash parameter, usually included as the last parameter at the method declaration. It can also take an arbitrary number of arguments (actually, they are plain hash items).
However, in both of the above cases, the number of arguments is unknown when you declare the method. Hence, you can't make an unknown number of arguments "required".
More info on splat, double splat and required keyword parameters is available at this blog post.

Ruby unary operator `&` only valid on method arguments

as you might know, Ruby has a shorthand operator & which let's us easily convert a Symbol to a Proc. Example:
%w(a b c).map(&:upcase) #=> ["A", "B", "C"]
Would be equivalent to:
%w(a b c).map { |c| c.upcase } #=> ["A", "B", "C"]
And the explanation is that &:upcase is calling to_proc on :upcase to create a Proc object.
So, that pretty much explains what the operator does when it's used as the last argument of a method.
However, looks like it's not possible to use the operator anywhere outside the params of a method call:
:upcase.to_proc => #<Proc:0x007febbc85ab38(&:upcase)>
&:upcase => SyntaxError: syntax error, unexpected &
This would have been nice to use like this:
case number
when &:even?
# ...
when &:prime?
# ...
end
This works though:
case number
when :even?.to_proc
# ...
when :prime?.to_proc
# ...
end
In short, the unary operator & can only be used in the arguments of a method, for example in arr.map(&:upcase). What is the reason for that?
The & unary prefix ampersand operator "unpacks" a Proc (or an object that can be converted to a Proc by sending it the to_proc message) into a block, passing it as if it had been passed as a block. Only message sends can have block arguments, ergo the & unary prefix ampersand operator is only allowed for the last argument in an argument list to a message send.
Likewise, the "dual" unary prefix ampersand operator "packs" a block into a Proc and is thus only allowed for the last parameter in a parameter list of either a block or a method definition.

Last expression evaluated in Ruby

I have this class definition:
class Test
attr_accessor :state
def multiple_state=(times)
#state *= times
end
end
obj = Test.new
obj.state = 2
puts #{obj.multiple_state=4}
I thought the output is 8, coz that is the value of the last
expression evaluated in multiple_state. (?)
But the output is 4.
Is my understanding of last expression evaluated wrong?
Thanks.
Ruby's syntactic sugar for setter methods always returns the right side of the assignment, even if you do something else in your method. The Well-Grounded Rubyist puts it better than I could:
Setter methods don’t return what you might think. When you use
the syntactic sugar that lets you make calls to = methods that look like assignments,
Ruby takes the assignment semantics seriously. Assignments (like x = 1)
evaluate to whatever’s on their right-hand side. Methods usually return the
value of the last expression evaluated during execution. But = method calls
behave like assignments: the value of the expression ticket.price = 63.00 is
63.00, even if the ticket= method returns the string "Ha ha!". The idea is to
keep the semantics consistent. Under the hood, it’s a method call; but it looks
like an assignment and behaves like an assignment with respect to its value as
an expression.
The Well-Grounded Rubyist - Chapter 3.3.3

are Ruby's logical operators methods, like binary operators are?

I was wondering, are &&, and, ||, or base core unchangeable functionalities (like in other languages e.g.: php) or are they object methods like & <=> but defined some magical way
More details on my trait of thoughts:
[] & [10]
# => []
[].&([10])
# => []
"aaa".& 10
# NoMethodError: undefined method `&' for "aaa":String
note it say undefined method
...of course you can do.
true.& false
# => false
...but you cannot do:
true.&& false
# SyntaxError:
so If it's possibility to do
class String
# monkey patch. If you googled this don't use this in real world, use ruby mixins insted
def &(right_side)
# do something meaningfull
right_side
end
end
"aaa".& 10
# => 10 # ta-da!
is there (with some magic) possible to do:
class String
# monkey patch. If you googled this don't use this in real world, use ruby mixins insted
def &&(right side)
# do something meaningfull
right side
end
end
# => SyntaxError: (irb):13: syntax error, unexpected keyword_end
thx
These are the operators that cannot be (re)defined:
&&, || (AND, OR)
.., ... (range)
?: (ternary)
rescue
= (and **=, &&=, &=, *=, +=. -=, <<=, >>= , ||=, |=, ^=)
defined?
not
and, or
if, unless, while, until
The others, like (incomplete list) !, ~, +, -, **, *, /, %, >>, ==, != are implemented as methods and can be redefined.
David A. Black stated in his book:
[T]he conditional assignment operator ||=, as well as its rarely spotted
cousin &&=, both of which provide the same kind of shortcut as the pseudooperator methods but are based on operators, namely || and &&, which you can’t override.
Now to get into the reason please Look and read Why can't we override||and&&? and Operator Overloading.

Resources