This is fantastic! This is the kind of connection between OO and FP that I think should be heralded much more.
Finally tagless style is an important architectural style for Haskell programming since it allows you to pass algebra joining up into the Prolog solver. This is more than adequate for simple combinations of algebras and workable (with a little elbow grease and commentary) for more complex ones.
Edit: Also, that boilerplate reduction on slide 30-32!
Multiple dispatch solves the expression problem so intuitively and straightforwardly that it's often hard to remember why it was a problem in the first place. In Julia, for example:
That's all there is to it. Of course, it's actually the external dispatch that's crucial, not the multiple dispatch. (But multiple part is very handy for other things, especially in numerical work).
This seems like a likewise elegant solution. However, it's not immediately obvious to me how to make this work with a static type discipline. Are there languages that do this?
It seems like part of the benefits of the approaches described in the blog post is that the type system will catch incomplete extensions to the existing data or methods.
In Scala, for algebraic data types like expressions and addition, you'd just use pattern matching. That would encompass the example fully.
Scala has an "external" dispatch mechanism as well, basically a way to create new typed methods, namely pimping:
implicit class FooWrapper(foo: Foo) extends AnyVal {
def newMethod: Int = ...
}
val x: Foo = new Foo()
x.newMethod
These are all checked at compile time.
Note that the example given above is actually not multiple dispatch, but merely adding methods to a type. It differs from function overloading (a Java/C++) feature only syntactically.
Multiple dispatch would be dispatch on multiple type arguments, not simply one (which Julia has, but Scala does not).
Unfortunately, operators can't be externally extended in C#, so there is still a lot of wrapper creation, but extension methods can be used for that; e.g.
Signal<int> a = ..., b = ...;
var c = a.Bl() + b.Bl();
Bl is an extension method for signal ints that returns a wrapper around the argument that allows for access to the + method. Bl is overloaded for a variety of types to provide access to those wrappers.
I believe that a similar approach can also be used in C++ with the help of ADL [1] and some template magic. Though it may only work on expressions whose concrete type may be known at compile-time (which makes it pretty useless).
Hmm, but when you add a new type of expression, you still have to write the particular implementation of the functions for that type.
There are other approaches to generic programming where when you add a new datatype, all the already defined functions work, and if you add a new function, it automatically works for all the datatypes.
It seems like a nice pattern. For popularizing this to Java programmers, I think the name "parameterized factory" might work better. Parameterizing a factory for nodes of an AST by the type of node returned (which might not be an AST node at all if it's fully evaluated) seems like a nice idea.
I think that over time, as you add new node types, you'd end up with lots of interface types, so you'd have to go back and clean them up after extending the language. But it will make the transition easier.
Also, real languages have multiple node types (expressions, statements, types, etc) that aren't interchangeable,so that might result in lots of type variables.
It seems that this structure could be implemented in C# with inheritance (to add new entities) and extension methods (to add new operations) and var keyword to achieve type inference to verify correctness at compile time.
Finally tagless style is an important architectural style for Haskell programming since it allows you to pass algebra joining up into the Prolog solver. This is more than adequate for simple combinations of algebras and workable (with a little elbow grease and commentary) for more complex ones.
Edit: Also, that boilerplate reduction on slide 30-32!