All,
I was excited about some of the things Rene and Leo demoed at WWDC and at Laurent and Reuben's urging, I'm copying these from the forum posting area to the general area to start some discussion. I'll make a new thread for each idea... sorry for taking so long to get here - the day job has definitely been in the way.
All the ideas are in the spirit of language modernization and pretty much all of them are unashamed ripoffs of things in other languages. In a number of cases, they seemed (to me at least) to be logical outgrowths of things that, based on the WWDC presentations, will be in the 4.0 language spec.
So, the first idea; this idea is basically a ripoff of lambda expressions in other languages – call it “lambdas/private functions”. The need/attraction is based on the idea that 4GL/BDL is a somewhat wordy language and tends to need significant amounts of boilerplate relative to other languages due to strong typing tending to prevent broad reuse (a reason I’m looking forward to the Reflection API).
As I understand it, you’ve basically already added two capabilities that should make the idea easier:
1. VAR keyword for dynamic defines, in 4.0.
2. FUNCTION types already in 3.20.
I think these could be combined to allow for lambas/inner private functions. These would help condense cases where there is a lot of repetitive code but it’s small enough to dissuade declaration of a separate function. I think C# and other compilers essentially do the same in their internal implementations.
The following code suggests how it might look, with the function created either in a DEFINE statement or a VAR statement:
FUNCTION b(x INT, y INT) RETURNS INT
RETURN x - y
END FUNCTION
FUNCTION test()
DEFINE a FUNCTION(x INT, y INT) RETURNS INT
RETURN x + y
END FUNCTION
LET b = a(1, 2)
DISPLAY b # 3
VAR b = FUNCTION(x INT, y INT) RETURNS INT
RETURN x + y
END FUNCTION
DISPLAY b(1, 2) # 3
DISPLAY b(3, 4) # 7
DISPLAY OUTER.b(3, 4) # -1
END FUNCTION
I can understand there's overlap between this and a module private function but the idea still seems worthwhile for short snippets or expressions. From a compiler perspective, the functions “a” and “b” are generated as an anonymous type and function private to the declaring scope, then the VAR is defined to that type. The functions above are both private to test() – the scoping follows the current variable scoping rules. So the inner FUNCTION block would in essence be acceptable wherever:
1. A type declaration can be used
2. A function reference can be assigned using the FUNCTION keyword already in 3.20, i.e. “LET a = FUNCTION stuff”
Up for debate as to whether shadowing is allowed on the function names.
- If allowed, any existing outer function with the same name would be shadowed by default – the inner function executes in its place. You could allow referencing the outer function using a keyword name like OUTER as shown above…
- Or… it generates a compiler error if that’s not consistent with other aspects of the language.
- Or… it generates a compiler warning that could be disabled via directive.
I’m thinking if you don’t allow shadowing of variables, you wouldn’t allow it for functions for consistency… but worth thinking about tradeoffs since there are benefits to it, but at the risk of some reduced clarity and opportunity for creating hidden issues.
I think the above could give the following benefits:
- Easier to read code – use an expressive function name rather than repeating the underlying statement(s)
- Easier to maintain – small similar snippets are encapsulated
- Easer to document – document the lamba, not all its usages
- Reduced clutter – no need to have lots of little free-standing functions to achieve the effect
- Keeps small bits of related code together – the declaration is close to the uses
- Encourages modularization – make a lambda/inner function… and if it grows too big or is useful in a broader context, it can be “promoted” by cut/pasting the function to the higher level scope, making a TYPE, and replacing any defines with the type.
- If you can find a more “terse” declaration syntax that’s consistent with 4GL, I’d be for that! A key advantage of the idea is to reduce the amount of code needed to create the reusable code block – the shorter it is (while still being easy to remember/consistent) the more useful, I think.
PS – Using the rules I give above, the following would be legal (although the example is contrived):
TYPE t_adder FUNCTION b(x INT, y INT) RETURNS INT
RETURN x + y
END FUNCTION
DEFINE a, b t_adder
You could also do combinations that might allow you do to some interesting dynamic dispatching:
TYPE t_something FUNCTION(s STRING) RETURNS STRING
...
FUNCTION something_1(s STRING) RETURNS STRING
RETURN "Hello, ", s
END FUNCTION
FUNCTION something_2(s STRING) RETURNS STRING
RETURN s, ", Hello"
END FUNCTION
FUNCTION test()
DEFINE say_it FUNCTION (x STRING, y t_something) RETURNS STRING
RETURN y(x)
END FUNCTION
DEFINE a, b t_something
LET a = FUNCTION something_1
LET b = FUNCTION something_2
DISPLAY say_it("Leo", a) # "Hello, Leo"
DISPLAY say_it("Leo", b) # "Leo, Hello"
END FUNCTION