Monday, March 28, 2016

Interfaces - Part 1

One of the motivations for making Tangent compile into CIL was that it would make it dead simple to interop with the huge pile of .NET code out there in the world. I mean, I would love to rewrite a bunch of library code, but I expect a lot of it would be worse than the stuff that people researched for years to make.

And I'm lazy.

So instead, I'm going to do a whole bunch of work implementing a well known feature in a weird way for a language that nobody will ever use. \o/

The problem is that Tangent does not have subtyping. And .NET code has... subtyping. I mean, I want IEnumerable to work - that doesn't seem to be too much to ask. Tangent though has some challenges with subtyping. For one, subtyping adds a lot of ambiguity to the order of operations inference engine. When an expression could be a few different types, you have more branches in the possible options to take. Another is dispatch. For .NET interfaces and single dispatch, there is a clear path to the implementation. Since Tangent allows multiple dispatch, it'd be weird to have interfaces behave differently.

So, what do I want interfaces to do?

  • I want interfaces to be some contract that types can supply. "I don't care what type is passed in here, as long as it can be enumerated." 
  • I need interfaces to be able to be generic - I am not writing IntEnumerable, StringEnumerable, BoolEnumerable, and so on. 
  • I want interfaces to be multiply-implemented. Just because something is enumerable should not prevent it from being comparable.
  • I would like interfaces to be open. That is, if you wrote AwesomeParser and I have IParser, I should be able to adapt your implementation to my interface without changing your code.
  • It'd be nice if interfaces could support operators better. .NET interfaces suck at things like T+T => T.
Those are the goals. Now to work out the kinks of how I plan on actually doing this stuff. I need to come up with an idea that actually works. It needs to integrate nicely with the existing language. And it needs to get compiled into CIL that is at least a little performant. More to come in Part 2.

Sunday, March 20, 2016

Parser Refactoring

Not a lot of sexy work in the past few months. I wanted to add features - local variables, interfaces, .NET interop, something - but just found myself dreading going into the parser code to extend it. So instead, I spent a little time refactoring that to suck less.

I should note that I'm talking about the actual parser code for type and function declarations, not Tangent's fancy inference engine for function bodies. The parser code used to be just a series of functions. They took tokens, they returned an optional type, popping tokens as needed on success, perhaps calling other parser functions. When the grammar was a dozen or so rules, that was fine. It was easy to step through, and it was easy to unit test.

That quickly got squirrelly as I added generics, function params, product types, and sum types. The grammar is still only about two dozen rules, but they interact a lot more. This means each parsing function is doing more, and that added complexity was harming unit testing and my ability to jump back into the code after some time doing real work.

So, I went back to an old stand-by the compositional parser. One of the first things I did when I learned C# was to create a parsing framework akin to boost::spirit for the first version of Tangent. Not using that though. It generates full parse trees, which are awkward to work with. I just tossed together something similar, but specific to the existing structure in the Tangent code.

What this allows me to do is to define the grammar more declaratively. That makes it easier to see what's going on. And since the declarations get real ugly real fast, it pushes me to separate things out into testable, reusable chunks. And since I was smart enough to have a small set of regression tests, I could make sure that the refactoring didn't break anything I expected to work before.

The stable checkpoint is available at