Initially, i did not see much difference between any other language and a language designed for visualisation (NPL). Almost all of it is the same until i saw an example.
First lets understand how a function call works in NPL.
- Whenever a signal with the name of the function and its arguments is raised, it creates a GenericConnect.
- This GenericConnect looks at the symbol table for the function definition. If it is present, it proceeds furthur, else it gives an error.
- Next, it evaluates the expressions passed as arguments to the function to get the value of their arguments.
- Once the arguments are evaluated, it creates a BlockConnect which represents the function definition and sends in these arguments which are local values in that scope.
- Once the BlockConnect starts execution, it stores these values in the local symbol table and goes on to execute the function.
- Once the execution of the function is complete, it returns the return value to the GenericConnect.
This is the typical implementation of a function call mechanism in NPL. This model of execution is good as long as the function call does not have nested arguments and the function is recursive in nature. So, a (fact 5) seems to provide the right data for visualisation.
But when the function call has arguments which are themselves function calls, the visualisation is unintuitive when it is based on the exact execution shown above. For example, consider the expression, a function call
(func1 (func2 2) (func3 3))
Intuitively, we would think about this as func1 being called with the values of func2 and func3. We assme, that func2 and func3 are evaluated by func1(or after calling func1 but before executing func1 ). When the interpreter evaluates this expression, func2 and func3 are evaluated before the func1 is called. The visualisation also shows the evaluation in the same order. This kills the intuitiveness of the visualisation compared to the textual form. Though this difference seems trivial it makes big impact on the quality of visualisation since i was completely clueless of what was happening when i saw the visualisation of the above expression for the first time.
Solutions:
So there are 2 solutions to the above problem.
- Make modifictions to the visualisation system such that we show that the evaluation of func1 starts the evaluation of func2 and func3. This is plausible for small changes, but is difficult when the functions are deeply nested. It is also computationally intensive as it involves lot of tree rewriting in the UI.
- Change the function call mechanism in the interpreter such that the evaluation of the arguments of the function takes place after function is called, but before the function starts execution. This seems easier to implement, but is slightly complex because, the arguments now need to be passed as closures.
I prefer solution 2 because it is more elegant and passing arguments is less computationally intensive. This is the reason, why i feel we need a seperate language designed for visualisation. Another example is the implementation of GenericConnect which is the topic for another day.