Work continues in fits and starts. I had gotten the language compiling using Expression Trees, which quickly ran into the issue that Expression Trees can't work with MethodBuilders, since they're not really MethodInfos. So that sucked. Not quite sure how previous iterations ever worked...
So things are now using Reflection.Emit to generate the actual IL, which seems like a particularly bad idea. It means I need to learn new things to make this already challenging adventure more challenging, and it means that things are likely to be even slower once running. But whatever. I don't need it to be fast, and I don't particularly want to build strings of C# and then recompile that.
Speaking of bad ideas, I've been working on getting conditionals working ("whoa, conditionals" you reply in your mock impressed voice). Yes, and sadly, I've gotten it into my head to not make conditionals built in statements, but something you can define in the language itself. And Tangent is now to the point where you can do that (I think, there's no way to easily execute the code yet):
bool :> enum { true, false } if (condition: bool) (positive: ~>void) => void { } if (condition: bool) (positive: ~>void) else (negative: ~>void) => void { negative; } if (condition: bool.true) (positive: ~>void) => void { positive; } if (condition: bool.true) (positive: ~>void) else (negative: ~>void) => void { positive;
}
So there are a few parts added since the last post to make this work. The first is the lazy operator ~>. It is a type constructor that takes a type and creates a "lazy" form of that type. In C# terms, it converts ~>void to Action, or ~>bool to Func<bool>. In Tangent terms, it allows you to pass in a fully bound function to the parameter without actually executing (reducing) it. And since everything in Tangent is a function, it allows you to pass things around unevaluated. Tangent does some trickery where a block is implicitly of type ~>void so they too can be passed around.
The other trickery is the bool.true sort of stuff. I'm not thrilled by the syntax, but the concept is likely to carry forward. This is the only current specialization/subtyping relation in the language. This sort of overloading is handled via dynamic dispatch by the compiler. Ideally as other subtyping relations show up, similar sort of specialization should be allowed. But that is for later.
For now, the dynamic dispatch and lazy evaluation is there in combination to allow you the user to create your own syntax for things as elementary as if statements.
Now to get enough working to actually run it and see some results.