Monday, October 20, 2008

Prediction I made :

I predict that in five years time, the hot language will either be a version of Python which has adopted ideas (Pattern matching arguments, Monads, immutability etc.) from Haskell; or a Haskell-like language which has borrowed some syntactic sugar from Python.


Don't know if I entirely believe that, but it's a good conversational gambit. And there's a certain logic to it if you look at the languages visually.

I'm personally, more into investigating Erlang at the moment, mainly because it seems more practical. The fast, parallel virtual machines are here and ready to go. But I could see myself looking back at Haskell too if it seemed to be gaining momentum.

I guess I also like the Erlang story on concurrency through message passing rather than transaction memories. And perhaps the whole typing thing is overdone in Haskell, although I can certainly see that it buys you more in that context than in, say, an OO language.

But I'd really love to see someone come up with mashup of the best bits of Python, Erlang and Haskell. It would *look* more or less like Python, although would find some-way to do multi-line lambdas etc. Like Haskell and Erlang it would have pattern matching arguments, functions defined over several definition statements, and immutable state. Like Erlang there'd be processes and messages as standard. Like Haskell, monads. Classes might be more like Haskell.

How might a language like this look?


class Tree(val,left,right)
def size(None) : 0
def size(Tree(_,left,right)) :
1 + size(left) + size(right)


Note that what's in brackets after the word Tree is neither a super-class (as in Python) nor a list of types as in Haskell nor a definitive list of field names. It's something more like information that a Tree is a tuple of three elements. And that by default they're called val, left and right.

These default names for the elements of the tuple can be over-ridden in functions which deconstruct a Tree using patterns. For example, the size function doesn't care about the initial val element and so leaves it blank.

I just thought of trying to make size a method of the class Tree. The syntax might then look something like this :


class Tree(val,left,right) :
def size(Tree(_,left,right)) :
1 + left.size() + right.size()


Where, obviously, the object itself is still (as in Python) explicitly passed as the first argument to the method, but instead of being called "self" we pick it up with the pattern of another Tree constructor and so it gets broken into _,left and right.

But where do we put the None / empty tree clause? My first thought was something like this :


class Tree(val,left,right) :
def size(None) : 0
def size(Tree(val,left,right)) :
1 + left.size() + right.size()


But that's not quite going to work. If this really were a more statically typed language like Haskell, and we knew that left and right were always Trees, this would be OK. But as we're trying to go for Pythonic duck-typing we have a problem. We would still like left.size() to mean, "pass the method selector size() to the object left" and then let the class of left decide how to handle it. But where left is None we have no idea which class to use to call. We could have a multitude of classes which define size(None); which would we pick?

Note that this is not the pattern-matching's fault. We'd have the same problem here :


class Tree(val,left,right) :
def size(self) :
if self is None : 0
else :
1 + self.left.size() + self.right.size()




We could get around the problem by disallowing methods which take a single None argument, effectively forcing the programmer to write something like this :


class Tree(val,left,right) :
def size(Tree(_,None,None)) : 0
def size(Tree(_,None,right)) : 1+right.size()
def size(Tree(_,left,None)) : 1+left.size()
def size(Tree(val,left,right)) :
1 + left.size() + right.size()


But this starts looking somewhat clunky.

We might make it a bit less awkward by providing conditional evaluation to the left and right subtrees. Based on Python's current conditional expression syntax that would look something like this :


class Tree(val,left,right) :
def size(Tree(_,left,right)) :
1 + (left.size() if left else 0) +
(right.size() if right else 0)


Or maybe we have to accept a static type system closer to Haskell's. Perhaps the types, in this case, contribute to Haskell's terseness.

4 comments:

Gleber said...

Hi,

Take a look at Reia:

http://reia-lang.org/

It implements some of your thoughts ;) And since it is work in progress you could influence it a bit.

Best regards,
Gleb Peregud

Composing said...

Hi Gleber,

thanks

you're the second person to point this out to me. Very interesting.

I definitely want to know more about that.

BillSeitz said...

Have you looked at this performance comparison between Erlang and Stackless Python?

http://muharem.wordpress.com/2007/07/31/erlang-vs-stackless-python-a-first-benchmark/

Composing said...

Thanks Bill

Fascinating. I really need to try Stackless.