I was reading some code and I saw something along the lines of
module M
def +@
self
end
end
I was surprised that this was legal syntax, yet when I ran ruby -c
on the file (to lint) it said it was valid. -@
was also a legal method name yet when I tried *@
or d@
both of those were illegal. I was wondering what +@
means and why is it legal?
A method in Ruby is a set of expressions that returns a value. With methods, one can organize their code into subroutines that can be easily invoked from other areas of their program. Other languages sometimes refer to this as a function. A method may be defined as a part of a class or separately.
We call (or invoke) the method by typing its name and passing in arguments. You'll notice that there's a (words) after say in the method definition. This is what's called a parameter. Parameters are used when you have data outside of a method definition's scope, but you need access to it within the method definition.
The code def hi starts the definition of the method. It tells Ruby that we're defining a method, that its name is hi . The next line is the body of the method, the same line we saw earlier: puts "Hello World" . Finally, the last line end tells Ruby we're done defining the method.
Ruby doesn't treat the ! as a special character at the end of a method name. By convention, methods ending in ! have some sort of side-effect or other issue that the method author is trying to draw attention to.
Ruby contains a few unary operators, including +
, -
, !
, ~
, &
and *
. As with other operators you can also redefine these. For ~
and !
you can simply just say def ~
and def !
as they don't have a binary counterpart (e.g. you cannot say a!b
).
However for -
and +
there is both a unary, and a binary version (e.g. a+b
and +a
are both valid), so if you want to redefine the unary version you have to use def +@
and def -@
.
Also note that there is a unary version of *
and &
as well, but they have special meanings. For *
it is tied to splatting the array, and for &
it is tied to converting the object to a proc, so if you want to use them you have to redefine to_a
and to_proc
respectively.
Here is a more complete example showing all kinds of the unary operators:
class SmileyString < String
def +@
SmileyString.new(self + " :)")
end
def -@
SmileyString.new(self + " :(")
end
def ~
SmileyString.new(self + " :~")
end
def !
SmileyString.new(self + " :!")
end
def to_proc
Proc.new { |a| SmileyString.new(self + " " + a) }
end
def to_a
[SmileyString.new(":("), self]
end
end
a = SmileyString.new("Hello")
p +a # => "Hello :)"
p ~a # => "Hello :~"
p *a # => [":(", "Hello"]
p !a # => "Hello :!"
p +~a # => "Hello :~ :)"
p *+!-~a # => [":(", "Hello :~ :( :! :)"]
p %w{:) :(}.map &a # => ["Hello :)", "Hello :("]
In your example the Module just simply defines an unary + operator, with a default value of not doing anything with the object (which is a common behaviour for the unary plus, 5
and +5
usually mean the same thing). Mixing in with any class would mean the class immediately gets support for using the unary plus operator, which would do nothing much.
For example (using ruby <=2.2
):
module M
def +@
self
end
end
p +"Hello" # => NoMethodError: undefined method `+@' for "Hello":String
class String
include M
end
p +"Hello" # => "Hello"
Note that in this example you can clearly see from the error message that the +@
method is missing from the class
Note that the above example will be different from Ruby 2.3, as the unary minus and plus are defined for Strings since that version, and they refer to returning a frozen and unfrozen string from the original.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With