Class variables and constants

88 views
Skip to first unread message

Phil Hagelberg

unread,
Dec 27, 2010, 8:53:12 PM12/27/10
to mi...@googlegroups.com
It seems that setting class variables inside the body of a class has
no effect; they just end up as nil during method execution. Is this
something that's just not plumbed all the way through, or is it a bug?

I'd rather use a constant, but it looks like that doesn't work either:

./griva/della/ferrante/Start.mirah:13: undefined method
`transform_const_assign' for #<Mirah::AST::TransformHelper:0x11e3c2c6>
USER_AGENT = "Ferrante"

Any hints?

-Phil

Rib Rdb

unread,
Dec 28, 2010, 11:48:45 AM12/28/10
to mi...@googlegroups.com
Unfortunately we don't support either of these yet.  The best thing you can do right now is probably to define a static method that returns the constant.

Phil Hagelberg

unread,
Dec 28, 2010, 12:00:29 PM12/28/10
to mi...@googlegroups.com
On Tue, Dec 28, 2010 at 8:48 AM, Rib Rdb <rib...@gmail.com> wrote:
> Unfortunately we don't support either of these yet.  The best thing you can
> do right now is probably to define a static method that returns the
> constant.

OK, good to know. How much work would it be to wire them in? I'm not
above some compiler spelunking myself if I can get a few pointers.

-Phil

John Woodell

unread,
Dec 28, 2010, 12:08:39 PM12/28/10
to mi...@googlegroups.com
In my humble opinion, this is another example where Mirah should behave like Java, but with a friendlier syntax.

Phil Hagelberg

unread,
Dec 28, 2010, 12:16:09 PM12/28/10
to mi...@googlegroups.com
On Tue, Dec 28, 2010 at 9:08 AM, John Woodell <mando....@gmail.com> wrote:
> In my humble opinion, this is another example where Mirah should behave like
> Java, but with a friendlier syntax.

Are you specifically talking about class variables? I don't mind
losing them, but if so they should be disabled at compile time rather
than just always returning nil at runtime.

Or do you mean something about constant creation should be more like
Java? I don't actually know Java, so could you elaborate?

-Phil

Charles Oliver Nutter

unread,
Dec 28, 2010, 1:02:06 PM12/28/10
to mi...@googlegroups.com
On Tue, Dec 28, 2010 at 11:16 AM, Phil Hagelberg <ph...@hagelb.org> wrote:
> Are you specifically talking about class variables? I don't mind
> losing them, but if so they should be disabled at compile time rather
> than just always returning nil at runtime.
>
> Or do you mean something about constant creation should be more like
> Java? I don't actually know Java, so could you elaborate?

Class variables do work, but they don't appear to work properly in the
class body. For example, this works:

➔ mirah -e "class Foo; def self.set; @@bar = 1; end; def self.print;
puts @@bar; end; end; Foo.print; Foo.set; Foo.print"
0
1

But @@bar = 1 in the class body seems to fail, claiming it's trying to
access the private "bar" field from the "DashE" class. Probably an
issue with not setting the "current" type when entering a class body
properly.

Phil: You could probably start poking around starting with
lib/mirah/transform2.rb at line 417 (def transform_class_var) to see
if you can sort out what's not working. There's also
transform_constant at 336, which may be more what you want...

- Charlie

Rib Rdb

unread,
Dec 28, 2010, 1:24:05 PM12/28/10
to mi...@googlegroups.com
I think what we need to do is take all the class variable assignments in the class body and move them into the static initializer.  Once that's working it should be easy to add support for constants.

It'd also be nice to gather any instance variable assignments in the class body and make sure they're run at the beginning of all constructors.

John Woodell

unread,
Dec 28, 2010, 2:43:39 PM12/28/10
to mi...@googlegroups.com
Defining variables in the class body will be an appropriate departure from Ruby syntax.

Michal Hantl

unread,
Dec 28, 2010, 2:49:54 PM12/28/10
to mi...@googlegroups.com
Can you explain please?
--
S pozdravem, Regards
Michal Hantl

gtalk/jabber: mic...@hantl.cz
icq: 241813215

Nick Howard

unread,
Dec 28, 2010, 2:54:27 PM12/28/10
to mi...@googlegroups.com

So, syntax-wise, these would be equivalent?

     class Foo
       @bar = 1
     end

And

     class Foo
        def initialize
          @bar = 1
        end
      end

The only issue I have is that it might be confusing to people coming from Ruby, because in Ruby these are not equivalent, and we'd need to document that effectively.

On Dec 28, 2010 1:43 PM, "John Woodell" <mando....@gmail.com> wrote:

John Woodell

unread,
Dec 28, 2010, 2:56:31 PM12/28/10
to mi...@googlegroups.com
As ribrdb suggested, class variable assignments in the class body should be moved into the static initializer.

Michal Hantl

unread,
Dec 28, 2010, 3:00:07 PM12/28/10
to mi...@googlegroups.com
so we would need to do this:

class Foo
  def self.initialize
    @instance_var = value
  end

end

instead of

class Foo
  @instance_var = value

end

is that right?

If so, what is the motivation?

Thanks fo explanation.


On 28 December 2010 20:56, John Woodell <mando....@gmail.com> wrote:
As ribrdb suggested, class variable assignments in the class body should be moved into the static initializer.



John Woodell

unread,
Dec 28, 2010, 3:03:03 PM12/28/10
to mi...@googlegroups.com
No, the Mirah parser will do this for you.

Michal Hantl

unread,
Dec 28, 2010, 3:03:55 PM12/28/10
to mi...@googlegroups.com
Awesome :) Now its clear.

Rib Rdb

unread,
Dec 28, 2010, 3:06:08 PM12/28/10
to mi...@googlegroups.com
On Tue, Dec 28, 2010 at 11:54 AM, Nick Howard <n...@baroquebobcat.com> wrote:

So, syntax-wise, these would be equivalent?

     class Foo
       @bar = 1
     end

And

     class Foo
        def initialize
          @bar = 1
        end
      end

The only issue I have is that it might be confusing to people coming from Ruby, because in Ruby these are not equivalent, and we'd need to document that effectively.


Right.  I believe this is how it works in java. But you're right that it may be confusing for ruby users.  I guess we need to decide what code in the body of the class should mean.  I can see a few options:

* Just allow initialization of fields. Instance variables are set in the constructor, class variables in the static initialzer.  Other code in the class body is an error.  This is most like java.
* Run it at compile time in the compiler for meta-programming.  This would probably be most ruby like, but would probably be difficult to understand.
* Run it in the static initializer
* Run it in the constructors
 
Getting constants working is probably the most important task (just initializing them at the beginning of the static initializer).  Other fields can probably wait since you can already manually initialize them in the constructors or static initializer.

Nick Howard

unread,
Dec 28, 2010, 4:46:50 PM12/28/10
to mi...@googlegroups.com
On Tue, Dec 28, 2010 at 1:06 PM, Rib Rdb <rib...@gmail.com> wrote:
Right.  I believe this is how it works in java. But you're right that it may be confusing for ruby users.  I guess we need to decide what code in the body of the class should mean.  I can see a few options:

* Just allow initialization of fields. Instance variables are set in the constructor, class variables in the static initialzer.  Other code in the class body is an error.  This is most like java.
So, only simple things like:

class Foo
  @bar = 1
  @@baz = 2
end

would work? In this case, it would be nice to have static initializer
syntax for cases where your class variables require some setup, maybe
something like,

class Foo
  @bar = 1
  static do
    rand = java.util.Random.new
    @@baz = rand.nextInt
  end
end 
 
* Run it at compile time in the compiler for meta-programming.  This would probably be most ruby like, but would probably be difficult to understand.


running it at compile time would allow metaprogramming like

class Foo < Model
  %w(bar baz).each do |name|
    param name, text
  end
end

but things like

class Bar
  @@baz = rand.nextInt
end

might act weird--it's harder to understand and implement.
 
* Run it in the static initializer

code like:

class Bar
  @@baz = rand.nextInt
end

would work intuitively, but you couldn't mix code and macros like in running it at
compile time.
 
* Run it in the constructors

class Bar
  @@baz = rand.nextInt
end

wouldn't work,

but

class Biz
  @baz = rand.nextInt
end

would.


Given these options, I'd prefer having the class body act as a static
initializer, because I think that would be more consistent. I.e.,
class things happen in the class body, instance initialization happens
in the initialize method, and instance things happen in instance methods. 

 
Getting constants working is probably the most important task (just initializing them at the beginning of the static initializer).  Other fields can probably wait since you can already manually initialize them in the constructors or static initializer.

Also, what does the static initializer syntax look like in Mirah? I glanced through the tests and examples and couldn't find any examples. 

--
-Nick Howard
http://blog.baroquebobcat.com/


Rib Rdb

unread,
Dec 28, 2010, 5:10:58 PM12/28/10
to mi...@googlegroups.com
On Tue, Dec 28, 2010 at 1:46 PM, Nick Howard <n...@baroquebobcat.com> wrote:
On Tue, Dec 28, 2010 at 1:06 PM, Rib Rdb <rib...@gmail.com> wrote:
Right.  I believe this is how it works in java. But you're right that it may be confusing for ruby users.  I guess we need to decide what code in the body of the class should mean.  I can see a few options:

* Just allow initialization of fields. Instance variables are set in the constructor, class variables in the static initialzer.  Other code in the class body is an error.  This is most like java.
So, only simple things like:

class Foo
  @bar = 1
  @@baz = 2
end

would work? In this case, it would be nice to have static initializer
syntax for cases where your class variables require some setup, maybe
something like,

Right.  The current static initializer syntax is

def self.initialize
  rand = Random.new
  @@baz = rand.nextInt
end

You could also do

class Foo
  @@baz = generateRand
  def self.generateRand
    Random.new.nextInt
  end
end
 

class Foo
  @bar = 1
  static do
    rand = java.util.Random.new
    @@baz = rand.nextInt
  end
end 
 
* Run it at compile time in the compiler for meta-programming.  This would probably be most ruby like, but would probably be difficult to understand.


running it at compile time would allow metaprogramming like

class Foo < Model
  %w(bar baz).each do |name|
    param name, text
  end
end

Basically yes, but it's going to have to work differently from ruby so it could be confusing.  This example would probably need to look more like this:

class Foo < Model
  %w(bar baz).each do |name|
    eval { param `name`, text }
  end
end
 
but things like

class Bar
  @@baz = rand.nextInt
end

might act weird--it's harder to understand and implement.

Right. These would end up being class variables in the compiler extension.  I suppose we could still make it so simple instance variable initialization worked and other statements run as macros.  I'm not sure if that would be more or less confusing though.
After thinking about it more, I think the least confusing thing is going to be if you have to explicitly say when you want code to run.  Simple initialization like java allows would be a nice feature, but other statements in the class body aren't allowed.  If you want something complicated to run in static initialization you put it in self.initialize.  If you want to do metaprogramming, you'd need to call a macro to do it:

class Foo < Model
  @@a = 1
  @b = String(nil)

  def self.initialize
    @@bar = Random.new.nextInt
  end

  compiler do
    %w(bar baz).each do |name|
      eval { param `name`, text }
    end
  end
end
 
This way it's clear which context each piece of code will run in.

Michal Hantl

unread,
Dec 28, 2010, 5:34:45 PM12/28/10
to mi...@googlegroups.com

This bit is not clear to me:

Why write:
 
class Foo < Model
  def self.initialize
    @@bar = Random.new.nextInt
  end
end

Why not just have this?

class Foo < Model
  @@bar = Random.new.nextInt
end

If I wanted to write

class Foo < Model
  @@generator = Random.new
  @@bar = @@generator.nextInt
end

Then those command would just execute in the order that I wrote them, wouldn't they?


Second thing I don't understand - what does that single @ variable mean?

class Foo < Model
  @@a = 1 <-- static, right?
  @b = String(nil) <-- whats this?

end


Could you please explain these to me? Maybe I don't know enough Java to understand :)

Thanks!

Charles Oliver Nutter

unread,
Dec 28, 2010, 5:43:10 PM12/28/10
to mi...@googlegroups.com
A quick weigh in from me...

I think @@ should work everywhere to mean "class variable" or "static
variable". Code in the class body should probably only be implicitly
inserted in the class initialize (like the entire class body is an
implicit static { } block from Java). @var should not be allowed in
class body; it's perhaps the most confusing aspect of Ruby that @var
in a class body is an instance variable of the class, and @@var in a
class body is something different...a "class variable declaration".

So to summarize based on examples given:

> class Foo < Model
>   def self.initialize
>     @@bar = Random.new.nextInt
>   end
> end

Valid. Explicit static initializer.

> class Foo < Model
>   @@bar = Random.new.nextInt
> end

Valid. Implicit static initializer.

> class Foo < Model
>   @@generator = Random.new
>   @@bar = @@generator.nextInt
> end

Valid. Implicit static initializer.

> class Foo < Model
>   @@a = 1 <-- static, right?
>   @b = String(nil) <-- whats this?
> end

Invalid. @b has no meaning in a class body. @ vars are only instance
variables, and there's no containing instance.

class Foo
@@baz = generateRand
def self.generateRand
Random.new.nextInt
end
end

Valid. Static methods are available to be called from static
initializers, and @@baz = generateRand is in the implicit static
initializer.

Does that all sound reasonable? I think it's best to just make @ mean
instance and @@ mean static always, everywhere.

- Charlie

Rib Rdb

unread,
Dec 28, 2010, 5:45:57 PM12/28/10
to mi...@googlegroups.com
On Tue, Dec 28, 2010 at 2:34 PM, Michal Hantl <michal...@gmail.com> wrote:

This bit is not clear to me:

Why write:
 
class Foo < Model
  def self.initialize
    @@bar = Random.new.nextInt
  end
end

Why not just have this?

class Foo < Model
  @@bar = Random.new.nextInt
end

Good point.  I was trying to show that you could still put complicated initialization in the static initializer, but this example isn't complicated enough to require it.
 

If I wanted to write

class Foo < Model
  @@generator = Random.new
  @@bar = @@generator.nextInt
end

Then those command would just execute in the order that I wrote them, wouldn't they?

Yes.
 
Second thing I don't understand - what does that single @ variable mean?

class Foo < Model
  @@a = 1 <-- static, right?
  @b = String(nil) <-- whats this?

end


Could you please explain these to me? Maybe I don't know enough Java to understand :)

This is initializing an instance variable.  Again, this is a bad example though since everything's initialized to nil. 

class Foo
  @a = "a"
  @@b = "b"

  def initialize
   ...
  end

  def initialize(other:Foo)
   ...
  end
end

would be the same as
 
class Foo
  def self.initialize
    @@b = "b"
  end

  def initialize
    @a = "a"
    ...
  end

  def initialize(other:Foo)
    @a = "a"
    ...
  end
end

Rib Rdb

unread,
Dec 28, 2010, 5:54:40 PM12/28/10
to mi...@googlegroups.com
I agree that @ should always mean instance and @@ should always mean static.  (We should probably fix that @ in static methods means a static field now that @@ is working).

I think it would be nice to support instance variable initializers like java has, but I'd be fine to leave this out if it is too confusing.  See the example in my last email -- assignments to an instance variable in the class body get copied into all constructors. (or perhaps collected into a private final method that's called from all constructors).

Charles Oliver Nutter

unread,
Dec 28, 2010, 5:59:13 PM12/28/10
to mi...@googlegroups.com
On Tue, Dec 28, 2010 at 4:54 PM, Rib Rdb <rib...@gmail.com> wrote:
> I agree that @ should always mean instance and @@ should always mean static.
>  (We should probably fix that @ in static methods means a static field now
> that @@ is working).

Yes, they should simply be a compile error.

> I think it would be nice to support instance variable initializers like java
> has, but I'd be fine to leave this out if it is too confusing.  See the
> example in my last email -- assignments to an instance variable in the class
> body get copied into all constructors. (or perhaps collected into a private
> final method that's called from all constructors).

You make a good point. Hmm. I'm having trouble reconciling the fact
that the class body is both an implicit static initializer (a method,
where you can basically run any code) AND having instance variable
initializers moved from there into construction...

For consistency, it seems like we'd need to only allow variable
initialization in the class body. What would something like this do,
for example:

class Foo
@@a = true
if @@a
@a = false
end
end

It would be a pain to find any cases where an instance var isn't used
at the root of the class and turn them into errors...

- Charlie

Rib Rdb

unread,
Dec 28, 2010, 6:17:37 PM12/28/10
to mi...@googlegroups.com
On Tue, Dec 28, 2010 at 2:59 PM, Charles Oliver Nutter <hea...@headius.com> wrote:
On Tue, Dec 28, 2010 at 4:54 PM, Rib Rdb <rib...@gmail.com> wrote:
> I agree that @ should always mean instance and @@ should always mean static.
>  (We should probably fix that @ in static methods means a static field now
> that @@ is working).

Yes, they should simply be a compile error.

> I think it would be nice to support instance variable initializers like java
> has, but I'd be fine to leave this out if it is too confusing.  See the
> example in my last email -- assignments to an instance variable in the class
> body get copied into all constructors. (or perhaps collected into a private
> final method that's called from all constructors).

You make a good point. Hmm. I'm having trouble reconciling the fact
that the class body is both an implicit static initializer (a method,
where you can basically run any code) AND having instance variable
initializers moved from there into construction...

For consistency, it seems like we'd need to only allow variable
initialization in the class body. What would something like this do,
for example:

class Foo
 @@a = true
 if @@a
   @a = false
 end
end

Yeah, this should be an error. 
 

It would be a pain to find any cases where an instance var isn't used
at the root of the class and turn them into errors...

I don't think it's too bad.  If we're looping through to find field initializers we can also check that all the children are an allowed type.
 

- Charlie

Charles Oliver Nutter

unread,
Dec 28, 2010, 6:32:37 PM12/28/10
to mi...@googlegroups.com
On Tue, Dec 28, 2010 at 5:17 PM, Rib Rdb <rib...@gmail.com> wrote:
> I don't think it's too bad.  If we're looping through to find field
> initializers we can also check that all the children are an allowed type.

Maybe we can have it just be a syntax error; "instance variable
assignment must be rooted in class body" or something.

- Charlie

John Woodell

unread,
Dec 28, 2010, 6:45:44 PM12/28/10
to mi...@googlegroups.com
I like the idea that compiler errors and warnings teach would-be mirah developers how the language works. This is especially nice when we use "mirah -e" to experiment. I hope we can do something with syntax highlighting in redcar to identify improperly rooted instance variables?  

Nick Howard

unread,
Dec 28, 2010, 6:54:38 PM12/28/10
to mi...@googlegroups.com
So by improperly rooted instance variable, you mean an initializer that is in the class body, but not in its root? or put another way, is it the if statement that should be invalid, or that it contains an initializer?

which way should this be invalid?
a)
class Foo
  @@a = true
  @b = true if @@a
                 ^^^^^^^^  
end
Error: if statement not valid in class body
b)
class Foo
  @@a = true
  @b = true if @@a
  ^^^^^^^^^^
end
Error: instance variable initialization must be rooted in class body

Michal Hantl

unread,
Dec 29, 2010, 4:18:39 AM12/29/10
to mi...@googlegroups.com
Can someone show a good use case of initializing an instance variable in class body?

I haven't used it so I'm having hard time imagining one.

I think that being able to use both @ and @@ in class body makes things more confusing, becase they are all called at different time and scope. (And you can use @instance_var in the body and also have def initialize; @instance_var; end; <-- that looks like a recipy for hard to tackle bugs)


Rib Rdb

unread,
Dec 29, 2010, 11:01:39 AM12/29/10
to mi...@googlegroups.com
I think the place it is most important is when you have lots of constructors.  It can be easy to forget to initialize a field in one of the constructors. If you put the initialization in the class body, the compiler will make sure it gets called from all the constructors.

Phil Hagelberg

unread,
Dec 29, 2010, 12:11:30 PM12/29/10
to mi...@googlegroups.com
On Wed, Dec 29, 2010 at 1:18 AM, Michal Hantl <michal...@gmail.com> wrote:
> I think that being able to use both @ and @@ in class body makes things more
> confusing, becase they are all called at different time and scope. (And you
> can use @instance_var in the body and also have def initialize;
> @instance_var; end; <-- that looks like a recipy for hard to tackle bugs)

For what it's worth I agree; it seems like allowing this may not be
worth all the baggage and edge cases that come with it. Everyone knows
how initialize works; it's better to build on well-known and
well-understood constructs than to create new constructs with murky
semantics.

-Phil

Rib Rdb

unread,
Dec 29, 2010, 2:16:22 PM12/29/10
to mi...@googlegroups.com
This isn't creating a new or murky construct.  This is a well known feature from Java: http://download.oracle.com/javase/tutorial/java/javaOO/initial.html

It's not useful in ruby since there's only one initialize method, but it can be very useful in Mirah where you can have many overloaded constructors.  For example the Model classes have 6 constructors (and they're generated by a macro, so you have no other way to add code to them).

Consider this code:
class Foo
  @variable = "bar"

  def bar
    @variable
  end
end

puts Foo.new.bar

In ruby, bar will return nil and nothing will be printed.  This seems confusing to me.
In mirah, I'm proposing that this should print "bar". This seems more consistent with other languages (python, java, c++)

John Woodell

unread,
Dec 29, 2010, 2:33:32 PM12/29/10
to mi...@googlegroups.com
Agreed, when I think of Mirah as a friendlier syntax (or macro compiler) for writing Java, I expect to be able to initialize variables in this manner.

I appreciate that Mirah creates the constructors for me, that feels like a lot boilerplate when writing Java. I also appreciate that I can enforce the types that methods accept, which is obviously not possible in Ruby.
Reply all
Reply to author
Forward
0 new messages