Maybe a bug?

In Ruby I can use an instance variable anywhere in the project because even code at the main level Inherits from theBaseObject class. Since all code is technically a part of that class you can still use instants variables at least that’s my understanding of how it works and Ruby. In Crystal you cannot use an instance variable except inside of a user defined class it’s not allowed at the main level. Does Crystal not have a BaseObject? Am I seeing some sort of compiler bug or how is this supposed to work?

It is by design. The only global state are constants and class variables.

IIRC the codegen assigns top-level methods to Program which is a special case.

What would be your benefit of having ivars as top-level shared mutable state?

There’s no real benefit. I was just curious as to why it works in one language but not the other. Crystal actually helped me learn the difference. Not kidding that when I started Ruby I misused instance variables by using them everywhere not just inside user defined classes because it looked cool :joy:

Since the workaround is to use class variables, at least there we decide to implement less features :stuck_out_tongue:

I only miss that feature when copy and pasting snippets of code.

Yeah I noticed back when ULayer migrated from Ruby on Rails I had made that mistake a lot.

I’ve been reading Practical Object Oriented Design in Ruby an agile primer by Sandi Metz and learned I’ve been writing really bad classes and methods. I have much to reflect on and much more to rewrite :grimacing:

I don’t think this has anything to do with BasicObject.

The explanation is that in Ruby code that runs in the top-level belongs to an instance of an anonymous class which is called “main”:

$ irb
irb(main):001:0> self
=> main
irb(main):002:0> self.class
=> Object

That’s the reason you can use instance variables there: they belong to that anonymous object.

Also, defining top-level methods is just defining private methods on Object. That’s why you can call them from every other class, because all of them inherit from Object.

And in fact, top-level methods in Ruby can’t be called from within a BasicObject because Object inherits from BasicObject, and these top-level methods are added to Object, not BasicObject.

So, again, BasicObject is not the reason you can use instance variables at the top level in Ruby.

All that said, we could have done the same thing in Crystal too. But when we realized how it worked in Ruby, how top-level methods just pollute Object with private methods, we though it wasn’t nice. Plus if you call a top-level method that would have self as the object you are calling from, so many different instantiations based on where you call it from.

For example in Ruby:

$ irb
irb(main):001:0> def foo
irb(main):002:1>   self
irb(main):003:1> end
=> :foo
irb(main):004:0> class Foo
irb(main):005:1>   def bar
irb(main):006:2>     foo
irb(main):007:2>   end
irb(main):008:1> end
=> :bar
irb(main):009:0> class Bar
irb(main):010:1>   def bar
irb(main):011:2>     foo
irb(main):012:2>   end
irb(main):013:1> end
=> :bar
irb(main):014:0> Foo.new.bar
=> #<Foo:0x00007fb7b4006680>
irb(main):015:0> Bar.new.bar
=> #<Bar:0x00007fb7b10617e0>