So one question you might have about the visitor pattern is how come those accept and visit methods

never return a value?

How come they’re all void?

Well, in actual fact, there is a different construction of the visitor pattern, which does something

similar to a reduce operation.

Basically folding an entire tree of whatever elements you’re visiting into one single element and assuming

that element is homogeneous, meaning it’s the same type all over the place, you can actually implement

a different kind of visitor.

Now, for this particular demo, I’ve basically removed everything that we had previously because we’re

going to reconstruct our implementation of the visitor pattern, but we’re going to use different terminology.

So now instead of talking about visitors, we’re going to be talking about Transformers.

And instead of accepting a visitor, we’re going to talk about having reduce and transform operations.

So as before, let’s have an abstract class expression.

And this time around, instead of having an accept method, we’re going to have a reduce method.

And the interesting thing about this method is it’s going to be generic.

We’re going to have public abstract T reduce on T, and here we’re going to take an AI transformer,

AI transformer of T, let’s call it transformer.

Now, we haven’t defined this type yet, so we may as well do it now.

So an AI transformer is going to be an interface AI transformer of T and it’s going to be able to transform

different kinds of expressions.

So let’s fix this right here.

Now let’s reconstruct the data structures that we previously used.

So remember, we had double expression and addition expression, so those should be easy to do.

So we’ll have double expression.

Double expression is going to be inheriting from expression.

And this means that it’s going to have a reduce method.

But in addition, it will have a public read only double value like So we’ll have a constructor which

actually initializes this value.

Now in the transformer stage in the reduction rather, the transformer is going to do one very simple

thing.

It’s going to try and reduce a double expression to some type.

Now, we don’t know what this type is, so if you want to evaluate an expression, we can try reducing

the entire tree to a double or an integer.

If we are about to print an expression, we can reduce it to a string and so on and so forth.

So you can see there are plenty of different options here.

And once again, I made a spelling mistake here.

So this is once again double dispatch.

So it’s going to operate very similar to the way the classic kind of visitor is done.

However, it’s going to call on the transformer, which is this interface.

Now let’s make another class called addition expression, just like in the demo before we’re going to

do this.

And now what I’m going to do is I’m going to actually give a body to this interface.

So we’ll have a method for transforming a double expression.

De And we’ll have a method for transforming an addition expression, a E So as you can see, the transformer

can transform these expressions into different types which are defined by the T here.

And by the way, we can probably get away with making it covariant like so.

Now the idea is that you take a double expression or an addition expression and you can reduce it.

So you can take all of its contents, reduce that, and then reduce the actual expression, transforming

it into some type T, whether it’s a double or a string or something else.

And we’re going to take a look at some of the examples similar to the examples we had previously, as

in the example of calculating the value of the expression, as well as calculating a string which represents

this expression.

So coming back to the reduce method here, what we can do is we can return transformer dot transform

this.

So it’s the typical double dispatch.

It’s the same stuff that we’ve done before, except the terminology has changed.

And also now we do a return.

So whenever previously we relied on side effects, now we don’t rely on side effects Effectively.

What we’re doing right now is we’re working with Transformers, which are stateless because all of the

state is carried on the stack as these values are folded together and returned at some point in time.

So let’s make another class.

Well, we’ve already made the class addition expression.

Let’s have the members public read only expression left and right.

So once again, make a constructor which initializes both of them.

And once again we inherit from expression and we need to implement this interface member.

So.

Here.

What I’m going to do is I’m going to reduce the left and right parts and well, just just return basically

transformer, transformer, dot and that’s the spelling again.

So transformer, transformer, dot transform this.

So I’m kind of passing back control to the actual transformer.

This is just a equivalent version of this.

There is nothing else that’s happening here.

But remember, we cannot just take this method and write it in a single place and have it work, because

what’s critical for us is the type of the this reference.

So the type here is different from the type here.

That’s why we have to have this in place.

So now with this setup, what we can do is we can, for example, build an evaluation transformer.

That is something that takes a tree, an expression tree and folds it to a single number.

So a class evaluation transformer is going to implement an AI transformer of double because it kind

of transforms everything and reduces everything to a double.

So let’s implement the actual members.

So we need to be able to convert or transform a double expression to a double.

Now that’s actually easy because here you just return the value and we may as well use a slightly simpler

notation for this.

Now, in the case of the addition expression, well, here we have to reduce both the left and right

hand sides and return that return the sum of that.

So we say return addition expression, dot left dot, reduce this plus a dot right dot reduce this.

So this is how we would go ahead and do that.

And let’s actually try all of this out once again.

So I’ll make a simple expression where I’m just going to do one plus two is the simplest thing that

you can do.

Probably here.

So let’s make an expression expr equals new addition expression.

So this is going to be an addition expression composed of two double expressions one and two.

So let’s just duplicate this one and two right here.

So we have these two double expressions.

Now we can make it, which is going to be an evaluation transformer like so we can call, we can get

the results of our result equals expr.

That’s our expression.

Dot reduce ET.

So we’re using the transformer to actually reduce an expression to a single number and we can print

that number.

Let’s actually make an additional transformer which prints the whole thing as a string.

That’s also something that’s very interesting because you’ll see how everything collapses into a single

string.

Well, it should be fairly obvious stuff.

So here I’ll make a print transformer, which is going to be an AI transformer of string.

So let’s implement this.

And of course, for the double expression, we return the value to string.

So the string representation of the double value.

And for the addition expression, well what we do is we return basically just just an expression with

rounded brackets printing the left and right hand side.

And once again, if you want to print the left and right hand side, you have to perform the reduce

calls right here.

So you say a dot left dot, reduce this and here you do a dot, right, dot, reduce this.

Once again, a fairly fairly obvious stuff right here.

So now we can continue with our example.

We’ve already got the result.

We can also get a print value.

So var dt equals new print transformer.

So make a print transformer and then just say var text equals expression dot reduce.

And now we use the print transformer to get the actual value and then we can print this whole thing.

So here, for example, we can say the text is equal to result like, so now let’s actually run this.

Let’s take a look at whether or not it works.

And if it does work, then what kind of output we get.

Hopefully we get one plus two equals three.

And that’s exactly what we get right here.

So that that is excellent.

That means that everything is working correctly and things are working just fine.

Okay.

So I wanted to explain a bit why we’re using this terminology, why we’re talking about reductions and

transforms and whatnot.

One of the uses of this approach to the visitor pattern is that you can effectively replace an entire

tree that you’re traversing by a different tree.

So effectively, in addition to performing a reduce, you can also perform a map or indeed a map reduce.

So a map is something where you take a tree and you transform it somehow.

And let me show you a very simple just just the simplest transform.

Let’s suppose that whenever we encounter a double expression, we square the value.

That’s all that we do.

But we reconstruct a brand new tree where the values of double expressions are squared.

So it would look something like the following.

You would have a square transformer, a square transformer, which is going to be an AI transformer.

But this time round we’re not transforming into a double or a string.

We’re transforming into an expression.

So we’re effectively transforming one expression into another expression.

And once again, we implement the interface here.

And what I want to do is whenever I encounter a double expression, I return a brand new, completely

unrelated double expression where I take the original value and I square it.

So I say the value multiplied by the value.

And when it comes to transforming an addition expression, what I do is I construct a brand new addition

expression.

So return a new addition expression where I simply make a copy of whatever it is that we’ve encountered.

And this is a recursive operation.

So here I can say a dot left dot, reduce this and a dot right, dot reduce this.

So this is a rather convoluted way of doing a deep copy effectively.

And you can think of other ways of replicating a value.

For example, you just want to make a copy.

You can use the prototype pattern in all of its incarnations.

Take a look at the prototype pattern materials in the course, but essentially the squared transformer

just takes a tree and transforms it into another tree.

So let’s take a look at how that works.

So essentially I’ll make a square transformer s t equals new square transformer like so I’ll have a

new expression constructed from the original expression.

So here var new exper equals expert dot reduced.

And if you look at this var right here, it results in an expression.

Obviously that’s what’s happening here.

So we’re taking an expression tree and we’re transforming it into another tree full of expressions.

But those are brand new expressions and we can print this once again.

So here I can say text equals for example, new expression dot reduce using the print transform that

we’ve got already.

And here I can just print this out.

So if I print out the text here, well, let’s take a look at what happened, because remember we started

with one plus two and now the whole thing got squared.

So we have one plus four.

So this is how you can work with a slightly different variation of the visitor design pattern, where

instead of having void accept methods, you have these reduce methods and also notice that there is

a bit of magic here with generic generics.

So we’re using not just ordinary methods which return some predefined type, but we actually specify

the return type as a generic parameter.

And this allows us for great flexibility.

The only restriction, the only real limitation of this approach to this design pattern is that whenever

you call this reduction operation, all of the elements of the tree have to reduce to the same type.

So there isn’t a possibility, for example, where you have an expression tree and some of the values

reduce to, let’s say, integers and some of them reduce to double values, even though you can mix

and match those together, it’s impossible to have an API where you can sort of mix and match these

values sufficient with sufficient flexibility, shall we say.

But if you are happy with just everything being reduced to one type like a string or a double, then

this approach works just fine and it provides certain benefits.

Now the last thing I want to mention is that you can combine this approach with the approach of having

void methods, meaning you can have both reduce operations.

So you can have an interface for performing reduction operations and at the same time you can also have

your classic accept methods which do not return a value which rely on side effects.

So those are the two options which are available.

And it’s up to you to pick which one works best under the circumstances.

Play Play Play Play Stop Play Play Play Start Play information alert