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.
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
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.
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 LoxCallable
s. 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
?)