Page 1 of 1

Critiques & Suggestions

PostPosted: Sat Apr 12, 2008 9:00 am
by Peufeu
Well, always a pleasure to see a new language, and this one seems like it's well thought out and it's got balls !

Now, the questions.

1- Continuations

There is no doubt that continuations are an exceedingly powerful tool.

But to my great disappointment, modern languages don't implement them, so noone can use this great tool.

Even in the severely limited form of Python generators, they still rock. However, you must not just copy the python generators, you must embrace and extend them !!!
Fully generalized continuations are very complex to implement and I don't know if it is possible to do so on .NET. Perhaps using fibers and coop multitasking ?

Stackless Python is an excellent example.

However, a limited form is possible, which is much more powerful than simple generators. I'll call that yield chain.
In the example where a generator/iterator traverses a tree, you have the classical (I'm writing Python code) :

Code: Select all
def walkTree( self ):
   yield self
   for child in self.children:
      for grandchild in child.walkTree():
         yield grandChild


This sucks horribly. What we need is this :

Code: Select all
def walkTree( self ):
   yield self
   for child in self.children:
      yield chain child.walkTree()


So, basically, "yield chain <iterable>" has the compiler handle the mess (it could do something dumb like generating the for loop behind the scenes, or do something more involved like push the current stack and operate in the <iterable> stack, then return.

And with that, you almost get coroutines for free.

2- Continuations (again)

Stackless Python introduced the idea of control flow inversion to the masses.
Suppose you need to process a stream of information, like XML crap or you get messages over a pipe. Then you would like to implement some little units which take a stream of information at one end, process it, and output a stream of information which is then fed to another little unit.

Because the "modern" languages all suck and don't support inversion of the control flow, this usually ends up with some horrible callbacks, sometimes hidden in a class, but it still sucks. Usually you have to write your own stack and reimplement half of Lisp. Have you ever used Expat ? You know what I mean.

Example :

- Pull data from a source
- Process it
- Try to return it somehow to the "caller", which means you will have to interrupt your current process ("yield" comes in handy but you can't put it inside a function, it has to be in the 1st level of depth or you need to put for-yield loops everywhere unless you have yield chain) or call a callback to send the information, or just give up, multithread and put FIFOs everywhere.

Problem with this approach : the first unit in the chain usually has a pull-model (like reading from a file), but the second unit cannot have the same interface since it would not work with the first unit. Usually you get the first few units working on the pull-model, except they are actually functions executed once per record with state kept in a struct, then somewhere callbacks come into play, and the last units in the chain all use callbacks, and pain and suffering ensues.

Control flow inversion transforms it into this :

- Pull data from a source
- Process it
- Push result in a pipe

Where the pipe (like in Stackless Python) is actually just a small non-locking FIFO which simply handles switching from one coroutine to the other.

This is pure goodness when the record boundaries are different between input and output (like parsing a file or receiving packets over a socket and turning them into records).

And, this constructs supports loops and tees in the graph.

3- Mixins

Is this supported ? Or Multiple Inheritance ?

4- Object Model

I suppose it's the same as C#, is it ?

I mean, an extremely powerful feature of Python is the classmethod, which is great to write frameworks. The class itself being an object, lots of so called "patterns" become obsolete (like the Factory) since they end up being implemented in 1 line of code.

Also an extremely useful feature of classmethods is this, in messy pseudocode :

Code: Select all
class DBObject:
   @classmethod
   def select( cls , id ):
      sql = "SELECT * FROM %s WHERE %s = %%s" % ( cls.getTableName(), cls.getNameOfPrimaryKey() )
      row = db.query( sql, id )
      return cls().fromDBRow( row )


I will spare you the code for the derived classes implementing getTableName(), but you get the idea.

In PHP, for instance, deriving a class from DBObject and giving it a static getTableName() function will not work, because once PHP executes DBObject::select(), it forgets that the class is actually not a DBObject, but a derived class, so it will fail on getTableName() because the method is not implemented in the base class, because the base class is abstract !

On the other hand, Python does it right, and the base class' classmethod goes to get the table name from the derived class' classmethod.

With PHP you have to instantiate a dummy object that you throw away afterwards for inheritance to work ! (and of course replace all the static methods with plain methods, since even on an instance, static methods behave as above).

Re: Critiques & Suggestions

PostPosted: Sun Apr 13, 2008 11:43 am
by Charles
1 - Continuations I'm not delving into continuations at this point in time. We have generators and threads which are at least sufficient to write interesting, practical code. As you pointed out, implementing continuations in .NET is a bit of a research project. Then combine that with the fact that there are many other outstanding Cobra issues to look after, and you can see why I won't tackle this anytime soon.

Regarding "yield chain", I don't see why it's much more powerful. "yield chain <enumerable>" just seems to save you from "for x in <enumerable>, yield x". If we did add syntactic sugar for this, we could reuse the "for" keyword as in "yield for child.walkTree".

2- Continuations (again) Your thoughts here are interesting, but maybe we need a working example in Cobra and then a hypothetical example which shows the more elegant approach. Er, maybe by "we" I meant "I". :-)

3 - Mixins The closest we have to this right now are the extensions in 0.8. They need additional work as described in the release notes. Also, Cobra will be getting partial classes like in C#. But even after those two are complete, it could still be debated whether they are sufficient replacements for mix-ins given that neither is dynamic. The re:motion guys are working on this for .NET and presented at the Lang.NET 2008 conference:
-- re:motion site
-- re:motion Lang.NET 2008 slides
-- re:motion Lang.NET 2008 video

I haven't thought much about whether Cobra could or should provide any syntactic sugar for re:motion. Only that I should consider it at some point.

I was also given a bunch of links on "traits" and especially "traits in C#" which I have not yet digested.

4 - Object Model I totally agree with your thoughts here. I'm astounded that the .NET designers stuck with the C++ and Java style static methods. I could rant right here, but I'll save it for a blog post.

You're correct that Cobra's object model == C#'s object model. So "shared" is like "static" in C# and "shared" in VB.

Class methods originally come from Smalltalk where an object has a class and if you want to lookup a method on the object, you simply follow its class pointer to the class where the methods for that object live. The classes themselves are objects and have the exact same structure which means that message passing is uniform. The class of a class is called a metaclass for discussion purposes (well and there is really a metaclass at run-time as well).

I think we could provide a new keyword "meta" which would act like Smalltalk and Python class methods. You could refer to "this" and you could access other meta methods which could be overridden in subclasses. The implementation could be accomplished by having Cobra add the implicit parameter for "this" (a trick it already does for extensions). Cobra programmers would have to realize that these meta methods would look strange to C# and VB developers if their code were exported as a .NET library. The "shared" methods would still be available for "least common denominator" purposes.

class DBObject
def select(cls , id)
sql = "SELECT * FROM [cls.getTableName] WHERE [cls.getNameOfPrimaryKey] = %%s"
row = db.query(sql, id)
return this().fromDBRow(row)
# or
class DBObject
meta
def select(cls , id) is meta
sql = "SELECT * FROM [cls.getTableName] WHERE [cls.getNameOfPrimaryKey] = %%s"
row = db.query(sql, id)
return this().fromDBRow(row)


Your syntax is more Python than Cobra. You should write some more Cobra code. Maybe get the source. Maybe write some patches. ;-)