Critiques & Suggestions
Posted: Sat Apr 12, 2008 9:00 am
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) :
This sucks horribly. What we need is this :
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 :
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).
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).