Summary
Wherein I talk about the merits of type inference, the latest Cat release, unit tests, and miniature computers.

Advertisement

I just posted a considerably more stable version of Cat (version 0.7.1) online at http://www.cat-language.com which has a working type inference engine for both stacks (previously it only sort-of worked for trivial expressions on a single stack).

I have been retroactively adding unit tests. (I can already hear Michael Feathers saying "You should have been writing the tests first!"). While I am a proponent of test driven development (TDD) in theory, to be completely candid I didn't actually practice it. Now that I am developing in C#, I find it easier for some reason (perhaps less fear of writing in an OOP style ... the C++ community frowns on OO style code for some reason?!). Writing a type inference engine is of sufficient complexity that I found that I I didn't have much choice: write unit tests or give up. Adding unit tests even this late in the game was still a great idea! I managed to work out a lot of kinks very quickly (much more so than external test suites). It also has been forcing me to remove unneccessary coupling in the code base.

Not many languages support type inference, but it is very useful. In fact it is indispensible in to have type inference in a language like Cat which has a strong type system. In Cat there are two stacks, and every operation is a transition from these two stacks to two other stacks. As a result type notation looks like:

define add_and_load : (int int)()->()(int) { add_int load }

Clearly it is a bit silly if your language requires more complexity to express types than the actual code! Now in Cat you can write:

define add_and_load { add_int load }

and the interpreter will figure out the type for you. You can ask for the type explicitly using:

@add_and_load type_of

This is in fact only the tip of the iceberg. Cat types can quickly get quite very sophisticated because it supports polymorphism (the C++ kind, not the ML kind ... what is it called universal quantification or something?).

The type of the Cat if operation is rather involved:

define if : (bool (A)->(B) (A:any*)->(B:any*))->(B)

Here is what this means in English: the if operation requires two functions of the same type on the top of the stack, and below that a boolean value. If the value is false, then the first function popped off the stack is executed, otherwise the other one is executed.

So this might seem like nothing fancy on the surface but consider the following: the MSIL (Microsoft Intermediate Language) byte-code has a similar restriction on "if" assembly code instructions. It is just that in Cat this is expressed naturally using a type-system.

This is almost precisely why I invented Cat: it maps to and from assembly code and while maintaining a strong type system. It also cleanly maps to and from other imperative languages (like Heron ... see where I am going with this?). Of course Cat can do a lot of things most assembly code can only dream of.

> Now that I am> developing in C#, I find it easier for some reason> (perhaps less fear of writing in an OOP style ... the C++> community frowns on OO style code for some reason?!).

I think it would be more correct to say that they frown at _inappropriate_/unnecessary use of OO, such as using a member function for something that might be better done with a free function.

Take std::string, as an exmaple: Its design is widely derided, even in the community, itself, for its bloated interface. As Kevlin Henney said it: "If std::string looks like it was designed by a committee, it's because it was." :)

Instead, you might have a few core functions - those that are needed to maintain invariants, and leaving the rest (like searching, sorting, etc.) as free functions. There's a very good article about this here: "The C++ Style Sweet Spot" (http://www.artima.com/intv/goldilocks.html)

Unfortunately, mixing member functions and non-member functions lead to two different calling conventions, in C++, but it doesn't have to be like that.

Java programmers typically overuse OO, when going to C++ (I did it as well, having done Java for a while, and coming back to C++ several years ago), as there's for example no free functions in Java. The "static import" stuff that has been added more recently might be used to work around that, though.

> This is in fact only the tip of the iceberg. Cat types can> quickly get quite very sophisticated because it supports> polymorphism (the C++ kind, not the ML kind ... what is it> called universal quantification or something?).

I'm not sure what you're thinking of, and was it the C++ kind or the ML kind you were looking for the name of? Also, from the cat examples, I'm not sure which corresponding C++ polymorphism you were thinking of (if it wasn't ML), but anyway, one term for the inheritance-and-virtual-functions polymorphism is "inclusion polymorphism" (because the type used has to be included in the hierarchy), whereas templates/generics go by the name of "parametric polymorphism"/"unbounded polymorphism".

Concepts/type classes would then be a way to say "For all T where..." :)

I don't have much experience with type inference, myself (note that C++ will get more of this in the next version of the standard, with "auto"/"decltype"), but I'd like to become more familiar with Haskell/ML.

> <pre>> define if : (bool (A)->(B) (A:any*)->(B:any*))->(B)> </pre>> > Here is what this means in English: the if operation> requires two functions of the same type on the top of the> stack, and below that a boolean value. If the value is> false, then the first function popped off the stack is> executed, otherwise the other one is executed.

> I'm not sure what you're thinking of, and was it the C++> kind or the ML kind you were looking for the name of?> Also, from the cat examples, I'm not sure which> corresponding C++ polymorphism you were thinking of (if it> wasn't ML), but anyway, one term for the> inheritance-and-virtual-functions polymorphism is> "inclusion polymorphism" (because the type used has to be> included in the hierarchy), whereas templates/generics go> by the name of "parametric polymorphism"/"unbounded> polymorphism".> > I guess you might be thinking of templates, as they> essentially model the mathematical expression "for all> T..."> (http://en.wikipedia.org/wiki/Universal_quantification)

Naturally. Only the function template signature model universal quantification; what happens in the body depends on the properties of the types. It's this mismatch that "concepts" are meant to address:

template<Addable T>T double(T x){ return x + x;}

We then get something like "For all T where "T+T" yields a value of type T..."

Moreover, in the original two proposals, one of them had "usage based" concept specification, like you show here in your concept definition. However, in the subsequent joint proposal, they've gone for the other alternative, "pseudu signatures", so the concept might now be written as:

> > Yes, but with the current proposal> (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2> 042.pdf), you can also use the "shorthand" syntax I used:> > template<Addable T>> T double(T x)> {> return x + x;> }> > Moreover, in the original two proposals, one of them had> "usage based" concept specification, like you show here in> your concept definition. However, in the subsequent joint> proposal, they've gone for the other alternative, "pseudu> signatures", so the concept might now be written as:> > concept Addable<typename T>> {> T operator+(const T &);> }