class locals as class methods

T

Trans

So I find myself once again creating some classes that have
attributes/state. I have two options, either the class defines
a class method for the info, or in a superclass I define a method
that will set that info to a class var when called.

# Example A
class Thing1 < TheseThings
def self.mystate ; "what have you" ; end
end

or

# Example B
class Thing1 < TheseThings
mystate "what have you"
end

The first is rather, well, ugly. Although it makes sense, it's
what one does at the instance level all the time (i.e. defining
methods to fit the interface). The second is nice and dsl-ish,
but requires a method to be predefined in TheseThings which
actually creates a number of oddities, like how does one read
the info out, and still be able to set it to nil? If that's an
issue one is lead to instead define two class methods, and
having to use self.maystate = "blah blah", instead.

Now everytime I go about this kind of thing I actually find
myself writing:

# Example C
class Thing1 < TheseThings
mystate = "what have you"
end

as that just feels natural. But then I have to go back and fix
it, of course. So last night it hits me, why not? Why not have
class locals define class methods? So in this case, Example C
would basically be like doing Example A. Yes, I know there's
namespace overlap, but class locals are rare enough that I don't
think that's a show stopper. Maybe there are other problems I'm
not seeing, perhaps there's some OOP formalism that this rubs the
wrong way. I don't know. But I've given it more than "15 minutes"
and it's actually growing on me. Am I crazy?

T.
 
M

Mark Hubbart

So I find myself once again creating some classes that have
attributes/state. I have two options, either the class defines
a class method for the info, or in a superclass I define a method
that will set that info to a class var when called. [...]
as that just feels natural. But then I have to go back and fix
it, of course. So last night it hits me, why not? Why not have
class locals define class methods? So in this case, Example C
would basically be like doing Example A. Yes, I know there's
namespace overlap, but class locals are rare enough that I don't
think that's a show stopper. Maybe there are other problems I'm
not seeing, perhaps there's some OOP formalism that this rubs the
wrong way. I don't know. But I've given it more than "15 minutes"
and it's actually growing on me. Am I crazy?

I think meta-programming would suffer.

# a Foo that is sort of like a Bar
class FooBar < Foo
bar_methods = Bar.instance_methods - self.instance_methods
bar_methods.each{ ... }
end

FooBar.bar_methods => ?????

It would be like penalizing people for using metaprogramming techniques :(

Perhaps, if you just want to set attributes:

class Class
def set(name, value)
self.class_eval "def self.#{name}() @@#{name} end"
self.class_eval "@@#{name} = ObjectSpace._id2ref(#{value.object_id})"
end
end
==>nil
class Foo
set :foo, "yep!"
end
==>"yep!"
Foo.foo
==>"yep!"

I'm sure it could be condensed even further, if needed.

Off topic now, I thought there was an equivalent of
#instance_variable_set for class variables? It would seem not. I
wonder why.

cheers,
Mark
 
T

Trans

Mark said:
I think meta-programming would suffer.

# a Foo that is sort of like a Bar
class FooBar < Foo
bar_methods = Bar.instance_methods - self.instance_methods
bar_methods.each{ ... }
end

FooBar.bar_methods => ?????

It would be like penalizing people for using metaprogramming
techniques :(

Hmm... I would think that code would work just fine. I'm not suggesting
that local vars be displaced, if that is what you mean, only that they
overlap. So a class local var is a class method is a class local var.
It is the same namespace afterall (i.e. self resoves to the same thing)
so that's okay too. Am I understanding your example?

For reference I tried:

module Bar
def a ; ; end
def b ; ; end
def c ; ; end
end

class Foo
def x ; ; end
def y ; ; end
def z ; ; end
end

class FooBar < Foo

# local
foo_bar = Bar.instance_methods - self.instance_methods
p foo_bar

# class
def self.bar_methods
Bar.instance_methods - self.instance_methods
end

end

p FooBar.bar_methods

with the result:

["c", "b", "a"]
["c", "b", "a"]
Perhaps, if you just want to set attributes:

class Class
def set(name, value)
self.class_eval "def self.#{name}() @@#{name} end"
self.class_eval "@@#{name} = ObjectSpace._id2ref(#{value.object_id})"
end
end
==>nil
class Foo
set :foo, "yep!"
end
==>"yep!"
Foo.foo
==>"yep!"

I'm sure it could be condensed even further, if needed.

Sure, that's one fair way. There are a number of ways to do it, like
the simple first examples I gave. But all of them seem, well,
contrived. In other words, there's no sort of obvious standard to this
kind of thing. And worse to me, the solutions all look like they've
been hit up with an ugly stick ;-)
Off topic now, I thought there was an equivalent of
#instance_variable_set for class variables? It would seem not. I
wonder why.

Hmm... I don't know, maybe b/c this is less characters?

Klass.class_eval { @@x = "foo" }

Thanks,
T.
 
M

Mark Hubbart

techniques :(

Hmm... I would think that code would work just fine. I'm not suggesting
that local vars be displaced, if that is what you mean, only that they
overlap. So a class local var is a class method is a class local var.
It is the same namespace afterall (i.e. self resoves to the same thing)
so that's okay too. Am I understanding your example?
[snip example]

I don't think so. I was referring to intermediate local variables that
you use inside classes when metaprogramming. I think I elided too much
code there; I just didn't want to bother thinking something up to take
up the elipses :) I'm just saying that you don't necessarily want all
your temporary variables to be permanently accessible. Also, there are
garbage collection implications. local variables are not usually
thought to be hanging around forever.
Sure, that's one fair way. There are a number of ways to do it, like
the simple first examples I gave. But all of them seem, well,
contrived. In other words, there's no sort of obvious standard to this
kind of thing. And worse to me, the solutions all look like they've
been hit up with an ugly stick ;-)


Hmm... I don't know, maybe b/c this is less characters?

Klass.class_eval { @@x = "foo" }

Doesn't work for arbitrary variables. Maybe this can be done without a
string-based eval or ObjectSpace._id2ref, but I couldn't quickly
figure out how:

class Class
def class_attr_accessor( name )
self.class_eval "def #{name}() @@#{name} end"
self.class_eval "def #{name}=(val) @@#{name}=val end"
end
end

compare to attr_accessor, which could be defined in pure ruby without
the evil string-based-eval.

class Class
def attr_accessor(name)
attr_name = :"@@#{name}"
name_eq = :"#{name}="
define_method(name){ instance_variable_get(attr_name)}
define_method(name_eq){ instance_variable_get(attr_name)}
end
end

cheers,
Mark
 
L

Lionel Thiry

Trans a écrit :
So I find myself once again creating some classes that have
attributes/state. I have two options, either the class defines
a class method for the info, or in a superclass I define a method
that will set that info to a class var when called.

# Example A
class Thing1 < TheseThings
def self.mystate ; "what have you" ; end
end

or

# Example B
class Thing1 < TheseThings
mystate "what have you"
end


You can use the attr_* instead:

class Thing1 < TheseThings
class <<self
attr_accessor :mystate
end
end


The first is rather, well, ugly. Although it makes sense, it's
what one does at the instance level all the time (i.e. defining
methods to fit the interface). The second is nice and dsl-ish,
but requires a method to be predefined in TheseThings which
actually creates a number of oddities, like how does one read
the info out, and still be able to set it to nil? If that's an
issue one is lead to instead define two class methods, and
having to use self.maystate = "blah blah", instead.

Now everytime I go about this kind of thing I actually find
myself writing:

# Example C
class Thing1 < TheseThings
mystate = "what have you"
end

as that just feels natural. But then I have to go back and fix
it, of course. So last night it hits me, why not? Why not have
class locals define class methods? So in this case, Example C
would basically be like doing Example A.

That's actually how python works.

class Thing1(TheseThings):
my_state = "what have you"
print Thing1.my_state # output: what have you

Well, it doesn't really create a my_state method, and doesn't really create some
@my_state anywhere. But at use, it works the same as you'd want ruby to work.
Yes, I know there's
namespace overlap, but class locals are rare enough that I don't
think that's a show stopper. Maybe there are other problems I'm
not seeing, perhaps there's some OOP formalism that this rubs the
wrong way.

I haven't the ability to demonstrate you anything, but my intuition is that it
is not much adequate for ruby...
I don't know. But I've given it more than "15 minutes"
and it's actually growing on me. Am I crazy?

Crazy of wanting ruby to look like python? Err... ;)
 
D

David A. Black

Hi --

techniques :(

Hmm... I would think that code would work just fine. I'm not suggesting
that local vars be displaced, if that is what you mean, only that they
overlap. So a class local var is a class method is a class local var.
It is the same namespace afterall (i.e. self resoves to the same thing)
so that's okay too.

But if you can access it non-locally, then it isn't a local variable:

class C
x = []
end

C.x << 1

at which point it's unclear to me what happens when you do:

class C
puts x # is this [1], or undefined?
end

Local variables have the advantage of being 'scratch-pad'-like. You
don't have to worry about outside access, nor about using the same
variable name twice in different scopes at the same level. If these
things are no longer the case, then the change is pretty deep.


David
 
M

Mark Hubbart

Hi,

Local variables have the advantage of being 'scratch-pad'-like. You
don't have to worry about outside access, nor about using the same
variable name twice in different scopes at the same level. If these
things are no longer the case, then the change is pretty deep.

Thank you. You just did a much better job of explaining this than I
did; What you said is exactly what I was thinking :)

cheers,
Mark
 
R

Robert Klemme

Trans said:
So I find myself once again creating some classes that have
attributes/state. I have two options, either the class defines
a class method for the info, or in a superclass I define a method
that will set that info to a class var when called.

# Example A
class Thing1 < TheseThings
def self.mystate ; "what have you" ; end
end

or

# Example B
class Thing1 < TheseThings
mystate "what have you"
end

The first is rather, well, ugly. Although it makes sense, it's
what one does at the instance level all the time (i.e. defining
methods to fit the interface). The second is nice and dsl-ish,
but requires a method to be predefined in TheseThings which
actually creates a number of oddities, like how does one read
the info out, and still be able to set it to nil? If that's an
issue one is lead to instead define two class methods, and
having to use self.maystate = "blah blah", instead.

Now everytime I go about this kind of thing I actually find
myself writing:

# Example C
class Thing1 < TheseThings
mystate = "what have you"
end

as that just feels natural. But then I have to go back and fix
it, of course. So last night it hits me, why not? Why not have
class locals define class methods? So in this case, Example C
would basically be like doing Example A. Yes, I know there's
namespace overlap, but class locals are rare enough that I don't
think that's a show stopper. Maybe there are other problems I'm
not seeing, perhaps there's some OOP formalism that this rubs the
wrong way. I don't know. But I've given it more than "15 minutes"
and it's actually growing on me. Am I crazy?

Probably - but then again, aren't we all? :)

Ok, here's what I'd probably do:

class TheseThings
module ThingClassProperties
attr_accessor :mystate

# needed for further inheritance levels
def inherited(cl) cl.extend ThingClassProperties end
end

extend ThingClassProperties
end

class Thing1 < TheseThings
self.mystate = "t1 state"
end

class Thing2 < Thing1
self.mystate = "t2 state"
end

p Thing1.mystate
p Thing2.mystate

13:28:13 [source]: ruby /c/temp/ruby/meta-prog.rb
"t1 state"
"t2 state"

You can make that even smarter by defining property accessors on the fly
etc.

Cheers

robert
 
T

Trans

Actually I should go further with this...

The idea of a clean nomenclature is very appealing, which is one of the
resons the idea of this thread is appealing to me. But I do see the
point of the "scratch-pad" variable. What's interesting though, is that
it is this distinction that really matters: whether the var is
temporary or permanant. How do we mark that distinction now? Lo, it's
not as far from what I had imagined as it is, actually. You can define
just such a variable using an @ sign. Of course you still need to
create the accessor methods to go with it:

class X
@a = 1
def self.a ; @a ; end
def self.a=(x) ; @a = x ; end
end

But this is what gets me when I think about it. It is this form --the
accessor form, that requires all this extra footwork; not the
"scratch-pad" local vars. You'd think it were the local vars that were
most commonly used then, but they aren't. So it would seem preferable
to make the locals the ones with the extra syntax, either in defining
them like a := 1 or a prefix, like %a = 1 or something.

Yet, going further, we might take this down to the method level too. It
has been suggested before (and I think quite reasonably) that we could
have local methods:

class X
def a
def b
"1"
end
b
end
end

In this case #b is local to #a. Applying the idea of this thread in
this context would mean that local vars are local methods too. So,

class X
def a
b = "1"
b
end
end

would be equivalent to the previous example. Here, again the
"scratch-pad" critique posses an issue. In effect the local vars are
becoming persistant. So, we could make a distinction between the
persitant vs. non-persistant to deal with this, but in this case the
syntax it is not quite as clear b/c local vars are much more common in
this method context.

T.
 
T

Trans

Ok, here's what I'd probably do:

robert, thanks for the example, I like the looks off it and I will play
with it.

T.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,170
Messages
2,570,925
Members
47,464
Latest member
Bobbylenly

Latest Threads

Top