Friday, October 26, 2007

Lisped

A programming language is much like the languages we speak. If a sound is missing from a language, the speakers cant produce that sound. More interesting is the fact that they do not understand the value of that sound. The lives of chinese people are not any worse because of the inability to say 'R'. Ditto for the programming languages.
  1. Languages vary in power. This can be proved by nagation. Let us assume that "All languages are equal in power". If all languages were equal, there would be no need for new languages because a new language means the inability of existing languages.
  2. New languages are created by programmers, not by standards. (JAVA whiners should read http://www.paulgraham.com/javacover.html). Languages are much like food. You can fool customer by wrapping bad crappy food in pretty packaging once, not twice.
  3. A language should get out of your way. A hacker's time is very valuable and the language should respect that.
  4. There is always a simple way to do everything. An elegant language should take the user to the most natural solution. Even if it means twisting the standards.
We have  a cyclic problem here. To understand the power of a language you have to learn it. And to learn it you must be convinced about the weakness of your current language. But how can you identify the weakness when you dont know it is a weakness? Kind of recursive, isnt it? The best way is to just take the word of a better haker. So if a person X who is a great hacker, says that a language Y is great, trust his word and study Y.
It was when I started reading Paul Graham that i heard about Lisp. I coould see people appreciating it, praising it, see it in action but not understand it. It seemed like just a big pile of CARs and CDRs. It took me three months of reading to witness the first glimpse of its power. And it was worth waiting. It was when I solved a problem in it that i saw its real strength.

Problem: You have to write a function that takes two lists, fun_list (a list of functions) and arg_list (list of arguments). Your function should apply each function in fun_list on every argument in arg_list and return a list containing the list of result of each function.

The C way
What we write and call C++, isnt remotely close to C++. So i wont be calling this C++ code.
  • First question: How to represent a list of functions? I ll just use vectors. DAMN, now i have to read about the syntax of vector. And how do I represent functions? That too with unknown signature? DAMN again! no problem, I ll just restrict the signature to some predefined value and mention it in my documentation. Or i can define it a template, but how do you do that? DAMN again. The arg_list also has to be defined as a template list.(Refer to 5)
  • Lets try Google for syntax issues (Refer to 3). Notice that we have already spend a good 10 minutes and we are yet to write a single LOC.
  • Okay, now that we have gathered all the stuff, lets get on with writing code. Lets start by a simple code and expand it to full form. Lets assume the each function operates on integers and return an integer as well.
#include <vector>
using namespace std;
vector<vector<int>> apply_funcs_to_args(vector<int (*)(int)> fun_list,vector<int> arg_list)
{
vector res;
return res;
}
And i am stuck as i need to confirm the syntax of function pointers. What to do? Back to Google and MSDN.SHEESH, hope somebody is keeping time, this is hard work dude.
Now i need a function that will apply a function to the arg_list and return the result. Here it is,
vector<int> apply_func_to_args(int (*f)(int) f,vector<int> arg_list)
{

int i = 0;
vector<int> res;
for( i = 0 ; i < arg_list.size() ; i++ )
{

res.push_back(f(arg_list[i]));
}
return res;
}

I have to make sure that this function is defined before apply_funcs_to_args or it will not recognize it. Now my final code becomes,
#include <vector>
using namespace std;
vector<int> apply_func_to_args(int (*f)(int) f,vector<int> arg_list)

{
int i = 0;
vector res;

for( i = 0 ; i < arg_list.size() ; i++ )
{

res.push_back(f(arg_list[i]));

}
return res;
}

vector<vector<int>> apply_funcs_to_args(vector<ret_type (*)(arg_type)&gt fun_list,vector<int> arg_list)

{

int i = 0;

vector<vector<arg_type>> res;

for( i = 0 ; i < fun_list.size() ; i++ )

{

res.push_back(apply_func_to_args(fun_list[i],arg_list));
}
return res;
 
}
But to make it work with any data type, i have to change it to template.
#include <vector>
using namespace std;

template<class ret_type,class arg_type>
vector<ret_type> apply_func_to_args(ret_type (*f)(arg_type),vector<arg_type> arg_list)

{

int i = 0;

vector<ret_type> res;

for( i = 0 ; i < arg_list.size() ; i++)

{

res.push_back(f(arg_list[i]));

}

return res;

}

template<class ret_type,class arg_type>

vector<vector<ret_type>> apply_funcs_to_args(vector<ret_type (*)(arg_type)> fun_list,vector<arg_type> arg_list)

{
int i = 0;

vector<vector<arg_type>> res;

for( i = 0 ; i < fun_list.size() ; i++ )

{

res.push_back(apply_func_to_args(fun_list[i],arg_list));

}

return res;

}

This code assumes that the return type of every function is same. So how do we handle different return types? We can make a generic class Result and force each function to return a pointer to class inherited from that class? Notice how a language restriction has forced us to propagate a change in some other code.
Okay, I am done. Where is my Electra? What! i am not? I have to test it too,well too bad, i am exhausted, maybe tomorrow. Infact i dont want to look at the code again because its too ugly.

Let me try Lisp. I can directly jump to writing whatever i am thinking. 

(define apply_funcs_to_nums(lambda(fun_list arg_list) (map (lambda (f) (map f arg_list)) f_list) ))

And we are done. Dont believe it? I myself was shocked to see that the code worked on the first run. And it took me not more than 5 minutes to write it and test it (infact I really didnt have to test it). And I spent the next two days in high fiving everyone just because I was so happy :D. This code is bug free, evident, non-restrictive and makes no assumptions. Its short and its beautiful.
Imagine a person who can pull this kind of stunt in every LOC. Do you think such skill can be beaten by scale? Would you be willing to take him on as your competitor?  I know I wouldnt.