Crystal: Get constant from a variable pointing to a class

Created on 13 Sep 2019  路  13Comments  路  Source: crystal-lang/crystal

Is there a way to make this work other than by defining a method on each class?

class C
  CONST = 1
end

klass = C
p klass
p klass.new
#p klass::CONST
#p klass.CONST
#p klass.constant("CONST")
feature discussion lang

Most helpful comment

Also:

class Object
  macro const_get(const)
    {% begin %}
      {{@type}}::{{const.id}}
    {% end %}
  end
end

class Foo
  X = 1

  def self.x
    const_get(X)
  end
end

class Bar < Foo
  X = 2
end

p Foo.const_get(X) # => 1
p Bar.const_get(X) # => 2
p Foo.x            # => 1
p Bar.x            # => 2

All 13 comments

Probably never going to happen.

You should define a class method and access the constant through it. Then you can override that method in subclasses.

That's the correct way to do it in OOP. It works and there's no need to add more things to the language for just this thing.

That's the correct way to do it in OOP. It works and there's no need to add more things to the language for just this thing.

@asterite

  • Monkey patching another library is the correct way to do it?
  • Writing duplicate code for a construct that already exists is the correct way to do it?

Why does klass.new work at all if it's companion accessors don't work automatically? What purpose does the extra ceremony of writing methods serve? I don't see the logic.

Maybe copying OOP that way other languages have done it isn't the best option.

@postmodern just replace that with methods, and let subclasses implement them. The methods should return the actual class instances. And you will need a base type to be able to type it.

There are many languages without that feature, yet they have CRC logic. Crystal is turing complete.

If you can't live with Ary's suggestion, you can also hack your way around with an inherited macro:

abstract class Base
  macro inherited
    def foo
      FOO
    end
  end
end

class Sub < Base
  FOO = "hey"
end

Not saying I endorse doing this a lot.

@jhass No need for macro I think:

class Base
  def self.foo
    {% begin %}
      {{ @type }}::FOO
    {% end %}
  end
end

class Sub < Base
  FOO = "hey"
end

Also:

class Object
  macro const_get(const)
    {% begin %}
      {{@type}}::{{const.id}}
    {% end %}
  end
end

class Foo
  X = 1

  def self.x
    const_get(X)
  end
end

class Bar < Foo
  X = 2
end

p Foo.const_get(X) # => 1
p Bar.const_get(X) # => 2
p Foo.x            # => 1
p Bar.x            # => 2

@asterite since this Ruby-like Object#const_get macro is that simple... wouldn't it fit great in corelib?

Sure!

Just note that I couldn't find a way to do this by calling it on a class instance... it just works if the class is an explicit identifier.

Also, I'm starting to think that it would be great to have other reflection macros as methods too, like superclass, subclasses, ancestors, includers, methods, etc. Even though they might not be useful at runtime, they can help quickly debug or understand something.

I really need for const_get right now. When I have module Adapter, and some classes inside, like MySQL or PostgreSQL, and I want to get a class by identifier from config, like adapter: postgresql.

@AlexWayfer That can't be implemented, and will never will.

The macro const_get here must be passed an identifier known at compile time.

That is it will never work with a const_get like macro, one approach you can take is to build up a runtime registry (Hash), possibly automatically using things like the inherited marco such that you can do something like my_adatpter = adapters[config_value] where adapters is something like {"mysql" => MySQL.new, "postgresql" => PostgreSQL.new}

FWIW, I recently used the same mechanism that @jhass proposed above to build a plugin architecture for some software that I am working on. So far, so good. It seems to be working well enough. The inherited macro lets any plugin classes that are compiled register their existence with a registry class, so that the class can be looked up via a name, and instantiated as needed.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

asterite picture asterite  路  139Comments

chocolateboy picture chocolateboy  路  87Comments

xtagon picture xtagon  路  132Comments

akzhan picture akzhan  路  67Comments

farleyknight picture farleyknight  路  64Comments