Best way to replicate Java's anonymous classes?

I’m following craftinginterpreters.com, and in Java you can dynamically create a class following an interface by using it like new. What would be the best way to replicate this in Crystal?

Probably just do like you would if that wasn’t a thing. I.e.

module MyInterface
  abstract def value : Int32
end

class Foo
  include MyInterface

  getter value : Int32 = 123
end

Foo.new.value

So there are no anonymous classes in Crystal. You can make a type private so that it doesn’t leak outside the lexical scope.

Anonymous classes in Java are often used for defining callbacks, and in Crystal you can use blocks or procs for this instead.

2 Likes

Can you show an example? Everyone is making their best to guess what you mean.

Like in crafting interpreters, there’s a snippet of Java code that’s a great example:

Interpreter() {
    globals.define("clock", new LoxCallable() {
      @Override
      public int arity() { return 0; }

      @Override
      public Object call(Interpreter interpreter,
                         List<Object> arguments) {
        return (double)System.currentTimeMillis() / 1000.0;
      }

      @Override
      public String toString() { return "<native fn>"; }
    });
  }

LoxCallable is an interface that requires arity and call. Usually we just need the variable to be any class that implements LoxCallable, but instead of creating a new class by declaring it, we pass it in as an anonymous class. Makes LoxCallable sort of look like a class instead of an interface, which is why I guess Crystal doesn’t seem to have it. But what’s the equivalent? Just define a new class for each built-in function?

Yes, there are no anonymous classes in Crystal.

It seems @straight-shoota understood your question :slight_smile:

Yes this seems like it could very well be solved with procs. arity and toString probably don’t even need to be methods, they’re just properties. And then you just need a plugable handler which can be done by a proc.

struct LoxCallable
  def initialize(@arity : Int32, @name : String, &@proc : Proc(Nil))
  end

  def call
    proc.call
  end
end

globals = {} of String => LoxCallable
globals["clock"] = LoxCallable.new(0, "<native fn>") do
  puts "proc called"
end

globals["clock"].call

The example is somewhat simplified and doesn’t handle parameters and return value of the proc. That’s going to be more complex than in Java but that’s just a limitation of Crystal’s generics (you can’t have Array(Object) for example).

A very similar implementation can be found in crinja:

Callable is a module that’s used for user defined functions. It provides the interface. Callable::Instance is like LoxCallable in my example above and uses a proc to define the behaviour.

2 Likes

Thank you so much for the help everyone! Okay, I made something I call a CallableFactory mainly because it’s for making a bunch of LoxCallables. Some code is here:

class CallableFactory
  include LoxCallable

  def initialize(@arity : Int32, @name : String = "", &block : Array(Literal) -> Literal)
    @func = block
  end

  def call(interpreter : Interpreter, arguments : Array(Literal)) : Literal
    @func.call arguments
  end

  def arity : Int32
    @arity
  end

  def to_s(io)
    io << "<native fn"
    io << " " if @name != ""
    io << @name << ">"
  end
end

and this works well enough, and I just use the |(arg1, arg2...)| kind of syntax to handle arguments and arrays, was wondering if there was a way around that but there doesn’t seem to be. Is this the best alternative? (Also looking at the code from Crinja, should it be a constant inside LoxCallable? Like LoxCallable::Factory?)