Wednesday, October 10, 2007

Implementing Functional Programming in Visual Basic 6

Was bathing just now, when I had this idea... as some of you may know by now, I've been doing my own research into FP techniques to eliminate, or at the least, minimize side-effects that every software engineer dreads.

Realised that VB6 has 3 features that make it possible to implement the following FP techniques:

1. Variables, once assigned to, cannot be changed. (This helps to eliminate any side-effects due to unintended state changes.)
2. Lists (Can be emulated by the good Collection class).
3. Recursion (VB6 has it)

While writing a program today, I discovered that one of the best things about the VB Collection class is that you don't have to worry about null objects being passed into an algorithm.

Let me explain - if I write:

Dim C as New Collection
Dim Item as Object
.
.
.
for each Item in C
process(Item)
next Item


Then even if C contains nothing i.e. its count is zero, you don't have to do any special handling unlike the case below:

Dim Item as Object
.
. (many, many, many lines of code later)
.
process(Item) - oops! You forgot to initialize Item... so your program crashes ... not at compile time, but at RUNTIME. :(


One of the lousiest disadvantages about object-oriented programming is that you normally require explicit instantiation, and sometimes, initialization of your objects. And more often than not, you discover your mistake only when you are running the application... at some especially embarrassing moment when you're demo-ing to your most important client.



Ok, about the immutable variables (or in FP terminology, symbols), to implement them in VB6, you can write:

FPSymbol.cls
Option Explicit

private m_Value as Variant
private IsAssigned as Boolean

' Attributes
public property Let Value(V as Variant)
if Not IsAssigned then
m_Value = V
IsAssigned = True
end if
end property
public property Get Value() as Variant
Value = m_Value
end property


The IsAssigned value is critical to ensure immutability of the symbol. And the Variant data type in VB6 is priceless for FP implementers - it makes life so much easier, since it can accept any data type (albeit at a significant performance price).



So, to exploit the benefit of immutable symbols and lists, we need to use recursion techniques, since iterative loops, a traditional staple of imperatives everywhere, technically are impossible in FP languages.

Example:
function F(C as Collection, S as FPSymbol) as FPSymbol
if C.Count = 0 then
' termination condition
else
T.Value = C.Item(0): C.Remove(0)
F = S.Value + F(C, T)
end if
end function


(For performance benefits, please try to do tail-end recursions as far as possible!)



As of now, I still haven't figured out how to implement higher-order functions and the classic Map, Reduce and Filter functions in VB6. But I think it should be relatively easy.

I believe the principles that I suggested above are easily implementable in any imperative language, which makes it convenient to use in current software projects. Plus, the great benefit is that you don't have to learn an entirely new functional language - which would probably be unacceptable in today's marketplaces.

Haha. Hopefully I can finally slay the dreaded Side-Effect Bugs once and for all. It might be possible, you know!

But even if you don't use the traditional FP paradigms in programming, just using immutable variables and/or collections can make a good difference in eliminating certain classes of bugs.

5 comments:

Anonymous said...

However, if you are programming "in the marketplace", then you have to consider whether using the FP paradigm comes with a performance cost. And of course, being unorthodox will affect other team members who might have to read your code now/in future.

Anyways, immutable variables... if they can't be changed, why not use constants? Or if they can be changed, only internally, then use private variable, with only public GET accessor, no SET accessor?

And for recursion, it's not just a FP idea. Imperative programming uses a lot of it. Be careful with recursion, if you run out of stack memory, it will crash very badly.

Haha, I want to see how you implement higher order function! This must be the holy grail of the FP-in-imperative challenge.

Anonymous said...

hey bro, just dropping by your blog. have fun at work and we'll catch up over mac donald's ice cream cone soon. haha =)-joyce

Anonymous said...

hey bro...

perhaps this is one of the posts in your blog that I'll never understand...

Anonymous said...

wei: hey bro! glad you understand what i'm typing about...

Good point, esp the one abt other team members having to read my code. i've been thinking about performance, and realised that we don't even need to use Variant-like data types. OOP has the answer. (I'll demo later.)

In fact, the functional paradigm can implement OOP, and with the example that i'm going to post soon, OOP can do vice-versa. Actually, I think the OOP -> FP translation is very much more straightforward - and you don't even have to be versed in FP to understand the OOP implementation.

Frankly, in software engineering - most of the time, maintenance and flexibility takes greater priority over performance. it's much easier to throw in an additional gig of RAM and harddisk space, than to hunt for a mysterious side-effect bug in the bowels of some dank code.

But! i digress. with the OOP implementation that i've thought of, it's possible to do FP programming in OOP two ways.

A. Early object binding and dynamic programming algorithms which practically eliminates the performance penalty of FP. (I'll prove it later.)

B. Late object binding which slows performance, but increases flexibility.

But frankly, run-time binding's a moot point - it's applicable in both OOP and FP. So not an issue really here.

Readability - haha... i think any code, if sufficiently bad, in any language, in any paradigm, is unreadable. XD That's why I was thinking of the FP-style paradigm - to improve code readability.

Constants! A good idea! :) No - an excellent idea. :D

Recursion - that's an important issue. Stack overflow sucks. So'll have to investigate whether VB6 does optimization of tail-end recursion.

And yes! Thank God... i've finally figured out the Way to Do Higher-Order Functions. Objects with functions! Objects can simply serve as "wrappers" for the functions... the actual implementation is left to the reader as an exercise.

Extra challenge: try passing in functions using late-binding in VB6. :) Use the CallByName function.

In any case, Python, Javascript, PHP, C++ all can do function parameter passing. Yay!

The Order of the Higher Functions. Sounds very solemn and knightly. :P

Anonymous said...

joyce: yeah! :D

huanyan: lol... but we can talk about linux as always...