Feature #7149

Constant magic for everyone.

Added by Boris Stitnicky over 1 year ago. Updated about 1 year ago.

[ruby-core:47945]
Status:Open
Priority:Normal
Assignee:Yukihiro Matsumoto
Category:core
Target version:next minor

Description

I noticed that certain objects have constant magic: When they are first assigned to a constant, they acquire a name property equal to the constant name string. I only know about Class and Struct objects behaving this way. I like this behavior, because I like to be able to say something like:

Adenosine = ChemicalSpecies.new initial_concentration: 5.micromolar
Adenosine.name #=> "Adenosine"

I like it so much, that I wrote a library (I call it ConstantMagicErsatz) for myself that searches whole namespace for the new objects assigned to constants. But searching whole wild namespace has its pitfalls. It is a wildly difficult workaround to get the candy I want. I am dreaming about just being able to say:

class ChemicalSpecies
constant_magic true
end

and imbue ChemicalSpecies with the same constant magic ability that Class and Struct classes have. Could it be made possible, please?

History

#1 Updated by Nobuyoshi Nakada over 1 year ago

  • Status changed from Open to Feedback

What do you expect if the object is assigned to two or more constants?

#2 Updated by Ilya Vorontsov over 1 year ago

May be any hook can be implemented to make it possible in such a way?
onconstset{|const, obj|
def obj.name
const
end
}

#3 Updated by Boris Stitnicky over 1 year ago

nobu (Nobuyoshi Nakada) wrote:

What do you expect if the object is assigned to two or more constants?

Same behavior as with Class and Struct objects. I do not understand the implementation details, but it seems that for these objects constant magic has already been consistently implemented. Actually, the implementation consistency is the main reason why I am begging here for an official solution of this.

#4 Updated by Nobuyoshi Nakada over 1 year ago

Do you expect the following?

class Foo
Bar = 42
end
p (6*7).name #=> Foo::Bar

#5 Updated by Boris Stitnicky over 1 year ago

Not out of the box, only if the user turns it on:

class Fixnum
constantmagic # or constantmagic( true ); or const_magic(); etc.
end

But, oh, do I feel your point. Saying that naming 42 is stupid is not enough.
Giving objects - any objects, not just fringe cases like Numerics, Symbols,
Vectors etc. - wrong names is a sin, root of evil surrounding the true nature :)
They say the devil himself is perhaps skin alone. I revulse names, unless these
be good hash functions, like those of Tolkien's Ents. But alas, biologists
deserve not the name of their science: they indulge in dissecting and giving
names that obscure rather than identify. Should this be remedied at the language
level? Definitely. But there is no way we would see this done before the end of
the century, if at all.

So, sadly, I have to live in the world I find myself in, refrain from proposing
to prohibit constants altogether, and satisfy myself with proposals that seem to
even foster the evil of deficient synonyms. Going back to naming 42, please note
that the rope to hang oneself with is already there: This behavior is achievable
with present devices of Ruby. I am only begging to make the possible more efficient.
Searching whole namespace gives me goosebumps.

#6 Updated by Yusuke Endoh over 1 year ago

  • Target version set to next minor

#7 Updated by Boris Stitnicky about 1 year ago

I have put my library in public ( https://github.com/boris-s/y_support ), so I can now exemplify. After installing the gem:

require 'y_support/name_magic'

class Klass; include NameMagic end

UJAK = Klass.new

Klass.instance_names    #=> [:UJAK]

UJAK.name               #=> :UJAK

Klass.new name: :ANEC

Klass.instance_names    #=> [:UJAK, :ANEC]

Klass.instance( :ANEC ) == Klass::ANEC    #=> true

I am too busy using the library to be working on its documentation, sorry.
I use this all the time, in expressions such as

Length = Quantity.standard of: :L

the above creates a Quantity instance named :Length with physical
dimension :L

METRE = Unit.standard of: Length, short: "m"

the above creates a physical unit named :metre (constant assignment
alone is enough to convey the information about the unit name, and
hook is used to downcase :METRE to :metre), so that 1.metre and 1.m
both start working.

more examples in https://github.com/boris-s/sy/blob/master/lib/sy.rb

#8 Updated by Marc-Andre Lafortune about 1 year ago

  • Category set to core
  • Status changed from Feedback to Open
  • Assignee set to Yukihiro Matsumoto

I've never needed this, but I could envision a const_assigned callback. Whenever a constant is assigned to an object, then object.const_assigned("FullyQualified::Name") would be called.

In other words, the "magic" around Modules and Classes could be understood with the "equivalent" Ruby code:

class Module
  def const_assigned(name)
    @name ||= name
  end

  def to_s
    @name || "#<Class:#{object_id}>"
  end
end

I would guess that the runtime impact would be completely negligible.

In the examples given above, ChemicalSpecies could have a similar method. And yes, even (6*7).name could be "Foo::Bar" with:

class Fixnum
  @@reg = {}
  def name
    @@reg[self] || to_s
  end
  def const_assigned(name)
    @@reg[self] ||= name
  end
end

What do you expect if the object is assigned to two or more constants?

That would be entirely up to the implementer of const_assigned, but const_assigned would be called multiple times.

#9 Updated by Charles Nutter about 1 year ago

const_assigned is not a bad hook at all, imho.

Also available in: Atom PDF