Goal

I want a simple language (small specification, easy to learn), that allows implementation of efficient and easy to use APIs.

I'm going to assume compile time type checking (and thus static typing) is important to reach this goal. It's an assumption: if you don't like it, design your own language.

To allow implementing performant and easy to use collections APIs, generics are needed. If you want a great language without them, there is Go. Having them seems to be a simplicity/expressiveness tradeoff. That said, I assert we need good generic collections, and don't want to clutter the language spec (keep them in libraries!), and thus it must be possible to implement generic collections as libraries.

Is there even a good way to do generics? Unfortunately there are lots of ways, none of which are very nice in my opinion: which approach you want depends on the use-case. The C++ approach of you can do anything, have all the tools (and compatibility with a legacy language as well) is known to violate the small specification requirement, but works. Is there a way around this? I think there is, and that is the design used for badlang.

A very small set of carefully chosen tools should be able to provide what is needed. In badlang's case the selected tools are metaprogramming, using the language itself as the metalanguage (Symmetric metaprogramming), and Partial evaluation.

The idea is simple: when you want to create a function, you write code that when it executes, creates a function object, and binds it to a name. This is nothing new: tons of languages do this. In python:

def f(x): return 2*x

Writing this code does not (like it would in C, Go, Java, or most other compiled languages) create a function. In python, its running this code that creates the function. Its the same as:
f=lambda x : 2*x

In python, the same is true for classes: you run code to create classes. Everything is created at runtime, can be created in inner scopes, and can be closed-over. This is powerful, put it means types and functions are not available at compile time for type checking and optimization: type checking has to be done at runtime. Thus, we employ partial evaluation: When the types and functions only depend on data known at compile time, the partial evaluation will generate them then. This also means that the code using those types' type checking code is also deterministic, and will also be evaluated at compile time: the entire runtime overhead can be removed in these cases.

Since we will have first class types, we can pass them in as parameters, and create classes that close over them, such as generic collections. If you want a generic list, make a function which you pass a type, and it returns a type thats a list of that type. (Making that possible, may or may not be practical to do performantly, but lets assume it is.)

Examples

If successful badlang's feature set will allow implementing, among other things, these examples:

  • performant and type safe printf, checked at compile time. It will be implemented as a higher order function, called like printf("formatString")(arg1,arg2). Thus, printf can be passed only a constant string, allowing it to be evaluated at compile time, parsing the string, and returning a function that takes the correct arguments.
  • Generic containers. Functions can be passed types, and return derived types. For example, a function could take a Type, and return a Vector class of that type. This could work for maps, pointers, etc. Types can be generated and passed around programatically. VectorOf(Int) could return a vector of Ints Type for example. If the Type parameters are known at compile time, like other function, it can be evaluated then. Otherwise you get runtime creation of types, from the same source.
  • Interfaces. Go style implicitly implemented interfaces should be possible to implement as a library. A function that takes a list of methods can return a Type, which would be the interface Type. It could have a method which can be passed some value to pack it into a new instance of the interface (generating method tables as needed), or perhaps a function that takes a Type, and returns a function that converts instances of that type into that interface (so it can fail if methods are missing at compile time, assuming the types are known).
  • Dynamic dispatch via inheritance. It should be possible to implement something like C++ classes (vtables and such) as a library.
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License