Micro Focus | Supportline

Lambda The Ultimate Perform

COBOL.net from Micro Focus is such a powerful tool - I just keep raving about it!


Guy Lewis Steele, Jr.. "Debunking the 'Expensive Procedure Call' Myth, or, Procedure Call Implementations Considered Harmful, or, Lambda: The Ultimate GOTO". MIT AI Lab. AI Lab Memo AIM-443. October 1977


The above paper discusses how anonymous functions can provide a very flexible and powerful way of manipulating control flow in a program without the ugly and dangerous imperative notion of GOTO. COBOL has its own notion of GOTO, the PERFORM verb. However, PERFORM in modern COBOL can be very 'nice' and not have any GOTO like behaviour; for example:



perform varying counter from 1 by 1 until counter > 100
set str to String::"Format"("Hello World {0}" counter)
set anAction to new type "System.Action"
(
delegate
add 1 to delCounter
invoke type "System.Console"::"WriteLine"(String::"Format"("{0} -> {1}," type "System.Threading.Thread"::"CurrentThread"::"ManagedThreadId" delCounter))
end-delegate
)
invoke actions::"Add"(anAction)
end-perform

Nevertheless, it would also be very cool indeed have lambda functions (from Lambda calculus) in COBOL. The above example is a bit of a cheat, because it gives a hint on how to do exactly that in Micro Focus managed COBOL. A delegate is a way of referencing a method dynamically. I.E. the method it references is chosen at runtime, not compile time. This makes a delegate somewhat like a lambda. However, when a delegate is anonymous it has no method name associated with it, only code. An anonymous delegate is a lambda. The above block of COBOL is creating lambda functions.


Moving From Lambda To Closure


In reality, the anonymous delegates in MF COBOL are actually closures. It is a common misconception that all lambdas are closures. For a lambda to be a closure it must enclose state from where it is created. Anonymous delegates in MF COBOL enclose the local storage of the method in which they are created.


COBOL And Generics


MF managed COBOL works out the box with generics. Generics are just like templates in C++ and represent a compile time polymorphism mechanism. Thus, collections can be strongly typed (for example). This has the benefit of more secure coding practices and potentially faster runtimes:




01 actions type "System.Collections.Generic.List"[type "System.Action"].


The above local storage declaration (variable definition) shows MF COBOL.net generics syntax. The square bracketed type at the end of the declaration causes the compiler to define the List collection as being a list of System.Action objects. I am using System.Action as it is a general purpose delegate class the constructor of which acts as a very good way of creating general purpose anonymous delegates in COBOL.net.


Iterating over collections



perform varying anAction through actions
set aWorker to new "AdvancedFeatures.Worker"(anAction)
set thread-start to new "System.Threading.ThreadStart"(aWorker::"Work"())
set aThread to new Type "System.Threading.Thread"(thread-start)
invoke aThread::"Start"()
end-perform

This piece of code uses perform to iterate over the generic list we created earlier. It does this using the 'though' keyword. This perform looping construct also launches new threads. Those threads will then use the delegates which we created earlier.


Putting it all together


So here is a throw away example program which uses anonymous delegates as closures and multi-threading (the .net way) and generics with iterators. Please note that there is a full threading syntax build into MF COBOL as well; so if you do not want to use the .net class library you can use the MF COBOL syntax. I might blog on that another time.



$Set SourceFormat "FREE".
program-id. Program1 as "AdvancedFeatures.Program1".

environment division.
configuration section.
repository.

data division.
working-storage section.
01 manager type "AdvancedFeatures.Manager".
procedure division.
set manager to new type "AdvancedFeatures.Manager"
invoke manager::"RunWorkflow"()
goback.

end program Program1.

class-id. Manager as "AdvancedFeatures.Manager".
object.
method-id "RunWorkflow" public.
local-storage section.
01 thread-start type "System.Threading.ThreadStart".
01 aThread type "System.Threading.Thread".
01 anAction type "System.Action".
01 aWorker type "AdvancedFeatures.Worker".
01 actions type "System.Collections.Generic.List"[type "System.Action"].
01 str string.
01 counter binary-long.
01 delCounter binary-long.
procedure division.
move 0 to delCounter
set actions to new "System.Collections.Generic.List"[type "System.Action"]
perform varying counter from 1 by 1 until counter > 100
set str to String::"Format"("Hello World {0}" counter)
set anAction to new type "System.Action"
(
delegate
add 1 to delCounter
invoke type "System.Console"::"WriteLine"(String::"Format"("{0} -> {1}," type "System.Threading.Thread"::"CurrentThread"::"ManagedThreadId" delCounter))
end-delegate
)
invoke actions::"Add"(anAction)
end-perform

perform varying anAction through actions
set aWorker to new "AdvancedFeatures.Worker"(anAction)
set thread-start to new "System.Threading.ThreadStart"(aWorker::"Work"())
set aThread to new Type "System.Threading.Thread"(thread-start)
invoke aThread::"Start"()
end-perform
goback.
end method "RunWorkflow".
end object.
end class Manager.

class-id. Worker as "AdvancedFeatures.Worker".

object.
working-storage section.
77 aDelegate type "System.Delegate".

method-id new public.
procedure division using by value toCall as type "System.Action".
set aDelegate to toCall.
end method new.

method-id "Work" public.
procedure division.

invoke aDelegate::"DynamicInvoke"(null)

end method "Work".

end object.
end class Worker.

0 Comments:

Post a Comment