Is there an easy way to mimic generics with an accept method of a visitor pattern?
vitamin
vit at vit.vit
Thu Feb 18 14:26:37 UTC 2021
On Thursday, 18 February 2021 at 13:53:19 UTC, Paul Backus wrote:
> On Thursday, 18 February 2021 at 11:14:05 UTC, Mina wrote:
>> I'm following along with the crafting interpreters book
>> (https://craftinginterpreters.com) and it goes into
>> implementing a visitor pattern that returns generic types, so
>> implementing it in D came down to the accept method causing
>> undefined symbol error that goes away when changing it to
>> returning a concrete type, so here's what I've got working
>> (https://github.com/MKamelll/dlox/blob/main/source/loxast.d)
>> and here's the book's implementation
>> (https://github.com/munificent/craftinginterpreters/blob/master/java/com/craftinginterpreters/lox/Expr.java).
>>
>>
>> Thanks.
>
> In D, because generics are implemented using templates
> ("monomorphization"), generic methods can't be virtual and
> can't be overridden in child classes. As you've discovered,
> that means `accept` has to work entirely with concrete types
> rather than generic ones.
>
> One way to solve this (which is used in the D compiler's source
> code) is to have both `accept` and `visit` return `void` and
> put the result inside the visitor object as a member variable.
> For example:
>
> interface Visitor
> {
> void visit(Expr.Literal expr);
> // etc.
> }
>
> class AstPrinter : Visitor
> {
> string result;
>
> override void visit(Expr.Literal expr)
> {
> if (!expr.literal.hasValue) result = "nil";
> else result = lexLiteralStr(expr.literal);
> }
>
> // etc.
>
> string print(Expr expr)
> {
> expr.accept(this);
> return result;
> }
> }
>
> Another possibility is to use discriminated unions and
> tag-based dispatch (i.e., switch statements) instead of classes
> and virtual method dispatch. This would make it a bit harder to
> follow the book, but might be a better learning experience if
> you're up for a challenge.
Or combination of discriminate uninons and classes:
/+dub.sdl:
dependency "sumtype" version="~>0.10.0"
+/
import std.stdio;
import sumtype;
alias Expression = SumType!(
ExprValue,
ExprBinary,
ExprUnary
);
class Expr{
abstract Expression expression()pure nothrow @safe @nogc;
}
class ExprValue : Expr{
string val;
override Expression expression()pure nothrow @safe @nogc{
return Expression(this);
}
this(string val)pure{
this.val = val;
}
}
class ExprBinary : Expr{
string op;
Expr left;
Expr right;
override Expression expression()pure nothrow @safe @nogc{
return Expression(this);
}
this(string op, Expr left, Expr right)pure{
this.op = op;
this.left = left;
this.right = right;
}
}
class ExprUnary : Expr{
string op;
Expr expr;
override Expression expression()pure nothrow @safe @nogc{
return Expression(this);
}
this(string op, Expr expr)pure{
this.op = op;
this.expr = expr;
}
}
string printExpr(Expr expr){
assert(expr !is null);
static auto impl(E)(E e){
static if(is(E == ExprValue)){
return e.val;
}
else static if(is(E == ExprUnary)){
return e.op ~ printExpr(e.expr);
}
else static if(is(E == ExprBinary)){
return printExpr(e.left) ~ e.op ~ printExpr(e.right);
}
else static assert(0, "no impl");
}
return expr.expression.match!impl;
}
void main(){
// (1 + (- 2 ))
Expr expr = new ExprBinary(
"+",
new ExprValue("1"),
new ExprUnary(
"-",
new ExprValue("2")
)
);
writeln(expr.printExpr());
}
More information about the Digitalmars-d-learn
mailing list