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
Related
Ruby uses === operator on the case/when type execution style.Now It also known that Ruby depending on the type
of the thing present in the when clause, calls the respective .=== method.
Say when statement contains the class names, then the rule is - it will use Module#===, which will return true if the right side is an instance of,
or subclass of, the left side. One example with this context is:
Here instance of test occurs
obj = 'hello'
#=> "hello"
case obj
when String
print 'It is a string'
when Fixnum
print 'It is a number'
else
print 'It is not a string'
end
#It is a string
#=> nil
Here subclass of test occurs
num = 10
#=> 10
case num
when Numeric
puts "Right class"
else
puts "Wrong class"
end
#Right class
#=> nil
Now when contains String literals then String#=== is called, which in turn checks if left and right handside
literal are same(same chracter in same sequence) or not.
a = "abc"
#=> "abc"
case a
when "def" then p "Hi"
when "abc" then p "found"
else "not found"
end
#"found"
#=> "found"
The all logic is too cool. Now my query is with case/when structure -
How does ruby know if when holding class, or String literals or
anything valid at runtime?
or
What test does it perform before calling the respective .===
operator on the thing when holding currently.
EDIT
Before understanding the Case/when working principal,let me clear the below which when does while it gets its turn.
String.===("abc") #=> true
Because "abc" is an instance of String class. - Am I right?
Now I tried the below just to check who is whose super class.
10.class #=> Fixnum
Fixnum.superclass #=> Integer
Integer.superclass #=> Numeric
Numeric.superclass #=> Object
Humm. That means the below returns true as Fixnum is also the indirect subclass of Numeric. - Am I right?
Numeric.===(10) #=> true
But why then the below outputs contradictory to the above?
Numeric.===(Fixnum) #=> false
Trying to be more specific to my query as below :
When we are calling Numeric.===(10) and String.===("abc") . I think we are sending not "abc" and 10 rather "abc".class and 10.class.
10.===(10) #=> true
Numeric.===(10) #=> true
Now look at the above. Both return true. Does they output true on the same logic? I think NO. 10.===(10) is just like
10 ==(10) comparison. But Numeric.===(10) outputs true as class of 10 is the subclass of Numeric.
"abc".===("abc") #=> true
String.===("abc") #=> true
Now look at the above. Both return true. Does they output true on the same logic? I think NO. "abc".===("abc") is just like simple string literal comparison "abc" ==("abc") comparison. But String.===("abc") outputs true as "abc" which is an instance of String.
Now my question is how ruby detects lefthand side operands types and apply the proper rule of comparisons ?
I might be 100% wrong, In that case please correct me.
I'll try to explain what #Lee Jarvis also explaines.
class Someclass
end
s = Someclass.new
p s.methods.sort
#[:!, :!=, :!~, :<=>, :==, :===, :=~, :__id__, :__send__, :class, :clone,(...)
Look at the 5th method. My Someclass instance has a ===method out of nowhere.
Actually, it has 56 methods and I did not define one of them. They are inherited from Object; every class in Ruby inherits from Object. For class Object (and my Someclass), #=== is effectively the same as calling #==, but (as the docs say) #=== is typically overwritten by descendants to provide meaningful semantics in case statements.
So ruby does nothing smart, it just sends the object in question a === message (or calls the ===method if you prefer that).
Now my question is how ruby detects lefthand side operands types and apply the proper rule of comparisons ?
It doesn't. It simply calls the === method. That's it. That's just how object-orientation works, and it is nothing specific to Ruby, every OO language works the same way: you call a method on an object, and the object decides how to react. (Or, in a class-based language like Ruby, PHP, Java, C#, C++, Python etc. the class of the object decides.)
Different objects may react in different ways. Classes check whether the argument is an instance of themselves, Regexps check whether the argument is matched by them, Ranges check whether the argument is covered by them.
It's just basic method dispatch.
Being new to Ruby, I'm having trouble explaining to myself the behavior around method definitions within Ruby.
The example is noted below...
class Foo
def do_something(action)
action.inspect
end
def do_something_else=action
action.inspect
end
end
?> f.do_something("drive")
=> "\"drive\""
?> f.do_something_else=("drive")
=> "drive"
The first example is self explanatory. What Im trying to understand is the behavior of the second example. Other than what looks to be one producing a string literal and the other is not, what is actually happening? Why would I use one over the other?
Generally, do_something is a getter, and do_something= is a setter.
class Foo
attr_accessor :bar
end
is equivalent to
class Foo
def bar
#bar
end
def bar=(value)
#bar = value
end
end
To answer your question about the difference in behavior, methods that end in = always return the right hand side of the expression. In this case returning action, not action.inspect.
class Foo
def do_something=(action)
"stop"
end
end
?> f = Foo.new
?> f.do_something=("drive")
=> "drive"
Both of your methods are actually being defined and called as methods. Quite a lot of things in Ruby can be defined as methods, even the operators such as +, -, * and /. Ruby allows methods to have three special notational suffixes. I made that phrase up all by myself. What I mean by notational suffixes is that the thing on the end of the method will indicate how that method is supposed to work.
Bang!
The first notational suffix is !. This indicates that the method is supposed to be destructive, meaning that it modifies the object that it's called on. Compare the output of these two scripts:
a = [1, 2, 3]
a.map { |x| x * x }
a
And:
a = [1, 2, 3]
a.map! { |x| x * x }
a
There's a one character difference between the two scripts, but they operate differently! The first one will still go through each element in the array and perform the operation inside the block, but the object in a will still be the same [1,2,3] that you started with.
In the second example, however, the a at the end will instead be [1, 4, 9] because map! modified the object in place!
Query
The second notational suffix is ?, and that indicates that a method is used to query an object about something, and means that the method is supposed to return true, false or in some extreme circumstances, nil.
Now, note that the method doesn't have to return true or false... it's just that it'd be very nice if it did that!
Proof:
def a?
true
end
def b?
"moo"
end
Calling a? will return true, and calling b? will return "moo". So there, that's query methods. The methods that should return true or false but sometimes can return other things because some developers don't like other developers.
Setters!
NOW we get to the meat of your (paraphrased) question: what does = mean on the end of a method?
That usually indicates that a method is going to set a particular value, as Erik already outlined before I finished typing this essay of an answer.
However, it may not set one, just like the query methods may not return true or false. It's just convention.
You can call that setter method like this also:
foo.something_else="value"
Or (my favourite):
foo.something_else = "value"
In theory, you can actually ignore the passed in value, just like you can completely ignore any arguments passed into any method:
def foo?(*args)
"moo"
end
>> foo?(:please, :oh, :please, :why, :"won't", :you, :use, :these, :arguments, :i, :got, :just, :for, :you, :question_mark?)
=> "moo"
Ruby supports all three syntaxes for setter methods, although it's very rare to see the one you used!
Well, I hope this answer's been roughly educational and that you understand more things about Ruby now. Enjoy!
You cannot define a return value for assignment methods. The return value is always the same as the value passed in, so that assignment chains (x = y = z = 3) will always work.
Typically, you would omit the brackets when you invoke the method, so that it behaves like a property:
my_value = f.do_something= "drive"
def do_something_else=action
action.inspect
end
This defines a setter method, so do_something_else appears as though we are initializing a attribute. So the value initialized is directly passed,
Take the String#=~ function for instance. It will return the index of the first match if the match is found, which, as a Fixnum will always act as true in boolean environments. If the match isn't found, it returns null, which acts as false.
Now suppose I have a class:
class A
attr_accessor :myprop
# prints "I am awesome" if #myprop matches /awesome/
# and "I am not awesome" otherwise
def report_on_awesomeness!
puts "I am #{myprop =~ /awesome/ ? 'awesome' : 'not awesome'}."
end
end
This code will pretty much work just as expected, but the first element in the trial conditional operator is the subject of my question.
Is it a good idea not to wrap myprop =~ /awesome/? I'm not talking about abstracting it into another method like def is_awesome?; myprop =~ /awesome/; end but rather whether my current convention, which forces Ruby to implicitly casts Fixnums to true and nils to false, is preferable over wrapping the condition into something I cast myself. I could easily do this:
class A
attr_accessor :myprop
# prints "I am awesome" if #myprop matches /awesome/
# and "I am not awesome" otherwise
def report_on_awesomeness!
puts "I am #{(myprop =~ /awesome/).nil? ? 'not awesome' : 'awesome'}."
end
end
Pros I see for the first style:
Most maintainers (including future me) are used to the implicit type
It's shorter
Pros I see for the second style:
It's more obvious exactly what the relationship is between the result of the =~ method and its boolean interpretation
It gives you more freedom to use more creative explicit casting
I suspect that there might be some middle ground, where you leave implicit type conversions in cases where it's idiomatic (e.g., regular expression matching using =~) and do it explicitly when it's not (e.g., your own properties, especially if they have multiple return types).
I would appreciate any insights or experiences the community can share on this issue.
IMHO that's a personal choice. You can take any style, since you feels better by working with that.
Once I defined true? on Object to get its boolean value (another name could be to_bool):
class Object
def true?
!!self
end
end
But the double bang (!!) is simpler to convert anything to Boolean and I prefer to use it - but not everywhere. I use it only when I need explicity a boolean value (I wouldn't use it in the case of this question).
BTW, false.nil? == false; it could lead to confusion.
Trying to do something weird that might turn into something more useful, I tried to define my own []= operator on a custom class, which you can do, and have it return something different than the value argument, which apparently you can't do. []= operator's return value is always value; even when you override this operator, you don't get to control the return value.
class Weird
def []=(key, value)
puts "#{key}:#{value}"
return 42
end
end
x = Weird.new
x[:a] = "a"
output "a:a"
return value => "a" # why not 42?
Does anyone have an explanation for this? Any way around it?
ruby MRI 1.8.7. Is this the same in all rubys; Is it part of the language?
Note that this behavior also applies to all assignment expressions (i.e. also attribute assignment methods: def a=(value); 42; end).
My guess is that it is designed this way to make it easy to accurately understand assignment expressions used as parts of other expressions.
For example, it is reasonable to expect x = y.a = z[4] = 2 to:
call z.[]=(4,2), then
call y.a=(2), then
assign 2 to the local variable x, then finally
yield the value 2 to any “surrounding” (or lower precedence) expression.
This follows the principle of least surprise; it would be rather surprising if, instead, it ended up being equivalent to x = y.a=(z.[]=(4,2)) (with the final value being influenced by both method calls).
While not exactly authoritative, here is what Programming Ruby has to say:
Programming Ruby (1.8), in the Expressions section:
An assignment statement sets the variable or attribute on its left side (the lvalue) to refer to the value on the right (the rvalue). It then returns that value as the result of the assignment expression.
Programming Ruby 1.9 (3rd ed) in section 22.6 Expressions, Conditionals, and Loops:
(right after describing []= method calls)
The value of an assignment expression is its rvalue. This is true even if the assignment is to an attribute method that returns something different.
It’s an assignment statement, and those always evaluate to the assigned value. Making this different would be weird.
I suppose you could use x.[]= :a, "a" to capture the return value.
I want to write a simple Ruby DSL to translate some statements and expressions into another language. A basic example would be:
some_function {
t + 2
}
Here, t is not a ruby variable and thus the block can't (and must not!) be evaluated by Ruby. So my best bet would be to use the parsing output (or AST) to do the translation myself. To do so, I can use ParseTree and ruby2ruby. However, I have other constructs that I would like to use, for example:
1.upto(10) {|x|
some_function {
t + x
}
}
Here, I have a local variable and I need to get its value in order to do my translation. However, in the function that does the block evaluation, I don't have access to the local variables of the calling block. If it were a global variable ($x), I could check if its name exists in the global_variables array and in the worst case use eval, but how could I do so for a local variable, if possible at all?
Update:
Just to clear up things. Like I said originally, I'm using ruby2ruby (and hence ParseTree) to get the AST (using to_sexp) corresponding to the block. But when using a local variable inside my block, I encounter the following:
[:dvar, :x]
And thus, I would need to get the value of a variable from its name as a string/symbol. And I can't use method_missing or instance_eval, because I want to translate the whole expression to another language or syntax (like a RPN).
Another solution not based on ParseTree would be welcome nonetheless, since it apparently is not fully supported with Ruby 1.9.
To get the variable values, use the proc's binding:
def some_function(&block)
b = block.binding
p [b.eval("t"), b.eval("x")]
end
t = 1
1.upto(10) {|x|
some_function {
t + x
}
}
Here, t is not a ruby variable and
thus the block can't (and must not!)
be evaluated by Ruby.
Any reason for the "not evaluated" restriction? It seems like method_missing would elegantly handle evaluating the "missing" t variable, but Ruby would automatically dereference the x variable.
You can use instance_eval against an object with t.
class Context
attr_accessor :t
def initialize(_t)
#t = _t
end
end
def some_function(&block)
puts Context.new(1).instance_eval(&block)
end
1.upto(10) {|x|
some_function {
t + x
}
}