Forum: Ruby using an instance variable inside a method

Posted by FirstName Surname (anozerone)
on 2013-02-05 21:17
Hello.

I'm learning ruby 1.9 by reading the book "Programming Ruby 1.9
The Pragmatic Programmers’ Guide"

Actually i'm on the page 57 and i think something is wrong. Let's show
the involved code :

#---
# Excerpted from "Programming Ruby",
# published by The Pragmatic Bookshelf.
# Copyrights apply to this code. It may not be used to create training
material,
# courses, books, articles, and the like. Contact us if you are in
doubt.
# We make no guarantees that this code is fit for any purpose.
# Visit http://www.pragmaticprogrammer.com/titles/ruby3 for more book
information.
#---
class BookInStock

  attr_reader   :isbn
  attr_accessor :price

  def initialize(isbn, price)
    @isbn  = isbn
    @price = Float(price)
  end

  def price_in_cents
    Integer(price*100 + 0.5)
  end
  # ...
end
book = BookInStock.new("isbn1", 33.80)
puts "Price          = #{book.price}"
puts "Price in cents = #{book.price_in_cents}"


About the method price_in_cents, i think it should be written like that
instead :

  def price_in_cents
    Integer(@price*100 + 0.5)
  end

Simply because unless i'm really missing something, "price" is a class
attribute after all there, it seems that in this case the "@" is
implicit, but that doesn't look the appropriate way to write it, right ?
Anyway i've tested with and without the "@", the result is the same.

So is this a common practice in ruby or what ?

Thx for reading.
Posted by Colby Callahan (Guest)
on 2013-02-05 21:27
(Received via mailing list)
@price is an instance variable not a class variable ($price) I believe.
Posted by FirstName Surname (anozerone)
on 2013-02-05 21:43
According to the book "$" is supposed to be used for global variables.
I still don't get it :/

Quote from the book :

"There’s a common misconception, particularly among people who come from 
languages
such as Java and C#, that the attr_reader declaration somehow declares 
instance variables.
It doesn’t. It creates the accessor methods, but the variables 
themselves don’t need to be
declared—they just pop into existence when you use them. Ruby completely 
decouples
instance variables and accessor methods, as we’ll see in the section 
Virtual Attributes on the
next page."

The code involved is in this section, so it should be the key.
I just don't understand why they didn't write the "@", maybe just to 
prove what they said, but from my point of view it seems a bad practice.
Posted by Joel Pearson (virtuoso)
on 2013-02-05 22:05
$ = global
@@ = class
@ = instance

Ruby is clever enough to figure out that you mean self.price (using the 
attr_accessor for @price) when you state "price" in the method. Of 
course, you could create a local variable called "price" within that 
code which would temporarily override the reference.

For example, modifying the class:

class BookInStock
 def price_in_cents
    price = 1
    Integer(price*100 + 0.5)
  end
end

> book.price
=> 33.8
> puts "Price in cents = #{book.price_in_cents}"
Price in cents = 100
=> nil
> book.price
=> 33.8
Posted by FirstName Surname (anozerone)
on 2013-02-05 22:22
I see, i thought that you could use these accessors only outside the 
class, not inside it.
However i'm still not sure what's the point of using the accessor 
instead of directly the instance variable there.
I suppose it makes sense when a method shouldn't use an instance 
variable "as it is".

Thx.
Posted by Joel Pearson (virtuoso)
on 2013-02-06 00:36
Personally I like to reference the instance variables using their "@" 
form in order to avoid ambiguity in the code. Everyone has their own 
approach and reasoning though, that's one of the great things about 
writing code... the artistic license :)
Posted by Matthew Kerwin (mattyk)
on 2013-02-06 03:06
(Received via mailing list)
On 6 February 2013 07:26, FirstName Surname <lists@ruby-forum.com> 
wrote:

> I see, i thought that you could use these accessors only outside the
> class, not inside it.
> However i'm still not sure what's the point of using the accessor
> instead of directly the instance variable there.
> I suppose it makes sense when a method shouldn't use an instance
> variable "as it is".
>

Yeah, it's a DRY thing.

If there's no other logic in your accessor then `@foo` and `self.foo` 
are
interchangeable (one has an extra character -- more typing; the other in
theory has an extra function call -- more execution time and memory 
usage,
although this could well be optimised away for all I know.)

However if you have other code (assertions, last-minute coercions, etc.) 
in
the accessor, then `@foo` and `self.foo` do different things, and you'd
have to make sure you either duplicate that code from self.foo (WETly) 
or
be certain that you don't need it in the case in question.

For myself, I use @foo when I'm thinking of the value as an instance
variable (literally a variable, some internal state used for 
calculations
or whatever, private to the object's scope), and `self.foo` when I'm
thinking of it as a "property" (an attribute that partially describes 
the
object, visible to the outside world.)

As a general rule I try to avoid bare `foo` since it's not clear whether 
I
mean a local variable or a property of `self` or something else 
altogether
(not to mention that `self.foo = 1` is very different from `foo = 1`).

Incidentally, I didn't realise the parser would recognise `price` above 
as
a function call; I thought lexically it looked like a local variable. 
Does
the attr_accessor directive make it more clever?  Are attributes 
lexically
different from function calls?  Am I overthinking something?

--
  Matthew Kerwin, B.Sc (CompSci) (Hons)
  http://matthew.kerwin.net.au/
  ABN: 59-013-727-651

  "You'll never find a programming language that frees
  you from the burden of clarifying your ideas." - xkcd
Posted by Ryan Victory (Guest)
on 2013-02-06 03:45
(Received via mailing list)
I'm certainly no expert on the Ruby interpreter, however I'd guess that
in order to support monkey-patching, multi file classes, and dynamic
dispatch/metaprogramming, the parser assumes that any part of the code
it can't find at the moment of parsing may be defined somewhere else and
leaves it alone until run time. Just my guess.

-Ryan Victory
Posted by Matthew Kerwin (mattyk)
on 2013-02-06 05:09
(Received via mailing list)
On 2/5/13 7:59 PM, Matthew Kerwin wrote:
> Incidentally, I didn't realise the parser would recognise `price` above
> as a function call; I thought lexically it looked like a local variable.
> Does the attr_accessor directive make it more clever?  Are attributes
> lexically different from function calls?  Am I overthinking something?

On 6 February 2013 12:44, Ryan Victory <ryan@raptormail.net> wrote:

>  I'm certainly no expert on the Ruby interpreter, however I'd guess that
> in order to support monkey-patching, multi file classes, and dynamic
> dispatch/metaprogramming, the parser assumes that any part of the code it
> can't find at the moment of parsing may be defined somewhere else and
> leaves it alone until run time. Just my guess.
>

Oh of course.  If there's no explicit variable creation (price = ...) in
the current context, it has to assume it's a function.  Don't mind me,
carry on.

--
  Matthew Kerwin, B.Sc (CompSci) (Hons)
  http://matthew.kerwin.net.au/
  ABN: 59-013-727-651

  "You'll never find a programming language that frees
  you from the burden of clarifying your ideas." - xkcd
Posted by Student Jr (student)
on 2013-02-06 05:20
The key here is "interpreter".  A bare token might either be a reference
to a local variable or to a method on self.  The interpreter interprets
the code as it is being run.  When it hits a bare token, it looks for a
method on self by that name.  If found, it calls it.  If not, it is
assumed to be a local variable.

And method bodies are definitely parsed ONLY when they are declared. 
Interpretation waits.  (Do not forget that 'method =' === 'method=' in 
the method lookup.

Note that it does NOT try method_missing.  This has major implications
if you are making heavy use of method_missing, as in TGWSNBN.

Using self.method is considered to be really bad form, unless you are
wanting method_missing to be invoked, in which case it is mandatory.
You see a lot of users of TGWSNBN writing junk like that.

As for attributes verses accessors, MK is doing it right.  Just because
o.m is defined, and o has an attribute @m, there is no particular reason
to assume that o.m == o.instance_variable_get(:@m).  There might be
important reasons that o.m = o.instance_variable_get(:@m) * 100 or
whatever.
Posted by "Jesús Gabriel y Galán" <jgabrielygalan@gmail.com> (Guest)
on 2013-02-06 12:40
(Received via mailing list)
On Wed, Feb 6, 2013 at 5:20 AM, Student Jr <lists@ruby-forum.com> wrote:
> The key here is "interpreter".  A bare token might either be a reference
> to a local variable or to a method on self.  The interpreter interprets
> the code as it is being run.  When it hits a bare token, it looks for a
> method on self by that name.  If found, it calls it.  If not, it is
> assumed to be a local variable.

This is not quite correct, although it's close. Running Ruby code has
two steps, the parsing and the runtime. At parsing time, each token is
labeled as either a local variable or a method call. If the parser has
seen the token at the left hand side of an expression before, it
labels it as a local variable. If not, it labels it as a method call.
At runtime, tokens labeled as local variables are evaluated to their
value, and token labeled as methods are looked up the method execution
chain to find them.

Jesus.
Posted by FirstName Surname (anozerone)
on 2013-02-07 00:51
Anyway i'm not a fan of implicit "not obvious" stuff, so i should write
"self." rather than letting the interpreter guess.

Thx all, it's pretty clear now.
Posted by Student Jr (student)
on 2013-02-07 01:56
Actually, there is another reason to avoid self.method.  self.method has 
the security level of protected--you can only access private methods via 
self.send.  Just say no.

And is sounds like Jesus has actually worked with deep documentation. :)
Posted by Robert Klemme (robert_k78)
on 2013-02-07 14:48
(Received via mailing list)
On Thu, Feb 7, 2013 at 1:56 AM, Student Jr <lists@ruby-forum.com> wrote:
> Actually, there is another reason to avoid self.method.  self.method has
> the security level of protected

Right!  Sometimes this is used intentionally to make method calls look
more like keywords - "attr_accessor" is such a case:

irb(main):006:0> Module.private_instance_methods.grep /attr/
=> [:attr, :attr_reader, :attr_writer, :attr_accessor]

>--you can only access private methods via
> self.send.  Just say no.

There are two ways to invoke private methods

foo()
self.send(:foo)

Note that the second also works with other expressions than "self".

Kind regards

robert
Posted by ernie n. (ernie_n)
on 2013-04-12 05:50
I'm a couple weeks as a Ruby newby ... writing a fun puzzle and ran into 
an issue I don't understand (read lots of books and searched ...).
==
class Board
  attr_accessor :type, :b, :p, :audit

  def initialize(type)
    @audit = Array.new
    @type = type            # either 9 or 10 depending on puzzle type!
    @b = Hash.new(:OOB)  # default for OOB
    @p = Array.new    # position of all the Pieces within the block
    if(type == 9)
      p[6] = Piece.new(6,1,1,:Obt)
      p[7] = Piece.new(7,2,1,:Obt)
    else
...
==
Why can I access p[6] without the "@" within the class?  I've run this 
in pry/irb and the p array looks the same regardless how I reference it 
- either @p or p.  What's up here?

Thx
Posted by Matthew Kerwin (mattyk)
on 2013-04-12 06:38
ernie n. wrote in post #1105353:
> Why can I access p[6] without the "@" within the class?  I've run this
> in pry/irb and the p array looks the same regardless how I reference it
> - either @p or p.  What's up here?

`@p` references the instance variable, and bare `p` is a method call.

`attr_accessor :p` defines a method called `p` which returns `@p` *

Thus, your `p[6]..` code is parsed as a method call `(self.p)[6]..`,
which is legal because that method (self.p) exists.

---

* note: it also defines a method called `p=` which assigns to `@p`, so
you can use `p = []` instead of `@p = []` if you want. C.f. attr_reader
and attr_writer

http://stackoverflow.com/questions/5046831/why-use...
Posted by Ryan Davis (Guest)
on 2013-04-12 10:37
(Received via mailing list)
On Apr 11, 2013, at 21:38 , Matthew Kerwin <lists@ruby-forum.com> wrote:

> * note: it also defines a method called `p=` which assigns to `@p`, so
> you can use `p = []` instead of `@p = []` if you want. C.f. attr_reader
> and attr_writer

No. you can use `self.p = []`, not `p = []`. That's the gotcha because 
the latter is treated as a local variable assign.
Posted by Robert Klemme (robert_k78)
on 2013-04-12 13:42
(Received via mailing list)
On Fri, Apr 12, 2013 at 10:37 AM, Ryan Davis 
<ryand-ruby@zenspider.com>wrote:

>
> On Apr 11, 2013, at 21:38 , Matthew Kerwin <lists@ruby-forum.com> wrote:
>
> > * note: it also defines a method called `p=` which assigns to `@p`, so
> > you can use `p = []` instead of `@p = []` if you want. C.f. attr_reader
> > and attr_writer
>
> No. you can use `self.p = []`, not `p = []`. That's the gotcha because the
> latter is treated as a local variable assign.
>

One more remark: it's probably not too good to name an attribute "p"
because the accessor will hide method "p" which is used for debug 
printing.
 Also, the name "p" doesn't really reveal any semantics.  I usually
restrict those names to internal use only but use more telling names for
public APIs.

Kind regards

robert
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.