Skip to Content
Technical Articles

Closures in ABAP

Closures are used in several programming languages, but are far removed from ABAP. That doesn’t stop the challenge of figuring out whether it can be done in ABAP, and I believe the answer is yes! This is the crazy beaten track part of what started off as one blog about my trying to implement a closure in ABAP. The sensible ABAP-Only learning part and deep dive into ABAP Memory is over here, and in this blog I’ll explain a little about closures and delve deeper into my experiment.

A closure is typically used in JavaScript and other languages, and is when a function works with a closed dataset outside its own scope. It is also closed in the sense that it is not global either. In a sense the data scope is something between an instance attribute and global variable.

The owner of the data needn’t even exist anymore. In JavaScript for example we can nest functions, and an outer function A can pass back an inner function B that works with the outer function’s data even after the outer function has completed. A nice step by step explanation is here.

Why would we do this? It is similar to an anonymous function, but it also allows delayed execution. In other words it acts like a callback function. Hence it’s an important feature of web-oriented languages where asynchronous execution is common. We can derive some data now, pass a function to someone for later execution. Sure this can all be done with ABAP objects in some way or another, but I wanted to implement the scoping model that makes a closure a closure.

*** DISCLAIMER *** : This is theoretical purely for educational purposes with no practical use in ABAP that I have been able to establish. I’m not advocating this as a technique to use in any real world program. I thought it would be fun to try to implement a feature found in JavaScript (and others) using ABAP, that’s all. A secondary goal was to figure out if there would be any practical use in ABAP.

Show me the Code

So how can we create a closure in ABAP? Note my original work was with a global and a local class fulfilling the role of the outer and inner functions, but for this blog I put everything into one report for simplicity. The end result is the same.

First we start with the ‘inner function’, represented by local class lcl_number. Its job is to do some arithmetic (method ADD) with a referenced number. In order to avoid dynamic programming with a local/global context, I also used an interface:

*--------------------------------------------------------------------*
INTERFACE lif_number.
*--------------------------------------------------------------------*
  METHODS add IMPORTING i             TYPE i
              RETURNING VALUE(result) TYPE i.
ENDINTERFACE.

*--------------------------------------------------------------------*
CLASS lcl_number DEFINITION.
*--------------------------------------------------------------------*

  PUBLIC SECTION.
    INTERFACES lif_number.
    CLASS-METHODS create IMPORTING i             TYPE REF TO i
                         RETURNING VALUE(result) TYPE REF TO lif_number.

  PRIVATE SECTION.
    DATA iref TYPE REF TO i.

ENDCLASS.

*--------------------------------------------------------------------*
CLASS lcl_number IMPLEMENTATION.
*--------------------------------------------------------------------*

  METHOD create.
    DATA(num) = NEW lcl_number( ).
    num->iref = i.
    result = num.
  ENDMETHOD.

  METHOD lif_number~add.
    ASSIGN iref->* TO FIELD-SYMBOL(<i>).
    <i> = <i> + i.
    result = <i>.
  ENDMETHOD.

ENDCLASS.

Next, we want the equivalent of the outer function, a global class (here just lcl_main) that has the data in an attribute. All it does it instantiate lcl_number with reference to its attribute i, and returns it to the caller.

*--------------------------------------------------------------------*
CLASS lcl_main DEFINITION.
*--------------------------------------------------------------------*

  PUBLIC SECTION.
    METHODS get_num RETURNING VALUE(result) TYPE REF TO lif_number.

  PRIVATE SECTION.
    DATA i TYPE i.

ENDCLASS.

*--------------------------------------------------------------------*
CLASS lcl_main IMPLEMENTATION.
*--------------------------------------------------------------------*

  METHOD get_num.
    result = lcl_number=>create( REF #( i ) ).
  ENDMETHOD.

ENDCLASS.

Now let’s have some fun:

*--------------------------------------------------------------------*
START-OF-SELECTION.
*--------------------------------------------------------------------*

  DATA o TYPE REF TO lcl_main.

  o = NEW lcl_main( ).
  DATA(num1) = o->get_num( ).

  o = NEW lcl_main( ).
  DATA(num2) = o->get_num( ).

  CLEAR o.

  WRITE / |Num1 add 1 three times: { num1->add( 1 ) }, { num1->add( 1 ) }, { num1->add( 1 ) }|.
  WRITE / |Num2 add 1 two times  : { num2->add( 1 ) }, { num2->add( 1 ) }|.

  write / 'Collect garbage'.
  cl_abap_memory_utilities=>do_garbage_collection( ).

  WRITE / |Num1 add 2 three times: { num1->add( 2 ) }, { num1->add( 2 ) }, { num1->add( 2 ) }|.
  WRITE / |Num2 add 2 two times  : { num2->add( 2 ) }, { num2->add( 2 ) }|.

Executing this, we see that num1 and num2 maintain independent counter variables that are external and detached from their parent.

Let’s go through this step by step:

First we instantiate lcl_main and ask it for an instance of lcl_number. This number object does not hold any data but works directly with the first lcl_main instance’s attribute i via a reference.

Then we overwrite o with a new instance of lcl_main and repeat. We end up with two instances of lcl_number that reference attributes of two different no-longer-existing instances of lcl_main.

We’ll add and write out the result a few times for each variable. Note how num1 and num2 each increment independently, since they were spawned by separate lcl_main instances.

We’ll also collect garbage, ensuring the lcl_main object instances are wiped out. Note how each lcl_number instance keeps its value, even though the lcl_main object no longer exists.

Conclusion

If anyone has any practical use in ABAP, do let me know. The closest I’ve come is perhaps a multi-window dynpro where the same values for multiple objects can be determined once and each window can work with it and react and pass instances around in events.

It would also be an interesting technique for better control over data, or overcoming some of the issues with static attributes, or enforcing mutability. Or we just do it because we can 🙂

The example source code is on GitHub over here

/
27 Comments
You must be Logged on to comment or reply to a post.
  • I always wondered why the ABAP team did not plan to allow STATICS variables inside instance methods, to be considered like instance attributes… SAP arguing that it should be declared as a static attribute instead (CLASS-DATA), because “This is a potential source of misunderstanding in instance methods”. I guess it was more a problem of “is it essential to have this feature versus the cost to adapt the compiler”. So, your code is the only way to bypass this limit.

    I am not familiar with the closures, but after reading the Javascript article you mentioned, I have a big doubt whether your code can be called a closure:

    “Global variables can be made local (private) with closures. […] A closure is a function having access to the parent scope, even after the parent function has closed.”

    I don’t think that instance methods can do something “close to a closure”. The concerned variables will always be public to all instance methods in the class.

    Closure could possibly be achieved inside static methods, artificially turned into instance methods by passing the instance as argument, and by declaring an internal table of the instances using the STATICS key word, which also contains the data. But I feel that it’s not even a good idea to do that, there are probably many issues in the real life. Here is an example to show the syntax of STATICS and object passed as an argument, the example is not functional for the rest:

    CLASS lcl_number DEFINITION.
      PUBLIC SECTION.
        CLASS-METHODS add 
              IMPORTING object TYPE REF TO lcl_number
                        inc    TYPE i                    
              RETURNING VALUE(result) TYPE i.
    ENDCLASS.
    
    CLASS lcl_number IMPLEMENTATION.
      METHOD add.
        TYPES : BEGIN OF ty_line,
                  object TYPE REF TO lcl_number,
                  number TYPE i,
                END OF ty_line.
        STATICS itab TYPE HASHED TABLE OF ty_line WITH UNIQUE KEY object.
    
        ASSIGN itab[ object = object ] TO FIELD-SYMBOL(<line>).
        <line>-number = <line>-number + inc.
        result = <line>-number.
      ENDMETHOD.
    
    • There’s also this for some reason, which is basically STATICS inside an instance method (though not what you want exactly, it’s still class-data not instance bound):

      CLASS lcl_test DEFINITION.
        PUBLIC SECTION.
          METHODS:
            test.
      ENDCLASS.
      
      CLASS lcl_test IMPLEMENTATION.
        METHOD test.
          DATA: lv_not_actually_local TYPE i VALUE 0 %_NON-LOCAL.
      
          lv_not_actually_local = lv_not_actually_local + 1.
          WRITE / lv_not_actually_local.
        ENDMETHOD.
      
      ENDCLASS.
      
      START-OF-SELECTION.
        DATA(go_test) = NEW lcl_test( ).
        go_test->test( ).
        go_test->test( ).
      
        DATA(go_test2) = NEW lcl_test( ).
        go_test2->test( ).
        go_test2->test( ).

       

      Output:

      Test
      
               1
               2
               3
               4
    • Hi Sandra,

      The big difference between your example of using statics and mine is that the data lives outside the object and class and is not managed or controlled by it. By destroying the outer/creating object, you are in a sense making global data private because it is now external to, but only accessible inside the created inner instance.

      The difference is subtle but significant in that there is no table and no management of instances. It all happens at instantiation time (hence deferred execution aspect), the outer object is then no longer necessary and the inner object and its class has no knowledge or accessibility of its parent or siblings. This provides more isolation than a managed static table.

      In essence, static attributes are not much different from globals, which is why they should be used with care. The only real advantage they have over globals is that they can be made private, but a public static is all but identical to a global.

      I stopped short of going into that detail in the blog, but by having separate instances of the outer class, we can have multiple instances of the inner class, each with their own ‘quasi-static’ attribute. Quasi-static, because it is only “static” to all instances created by the same outer instance, which is a key difference here.

      Consider the following instances of Parent and Child

      P1 instantiates C1 and C2, and

      P2 instantiates C3 and C4

      Now if C1 performs add, C2 will have the updated value, but C3 and C4 will not. In essence C1 and C2 are sharing one “static” and C3 and C4 another.

      It’s a contrived example, but consider a multi-window setup, with multiple items appearing in several windows. Perhaps something like SBWP. An outer instance for each item creates an inner instance for each window and is then cleared. Changes to one item in one window will reflect on the same item in the other windows, but with no lookup, indexing, statics or instance table involved. Yes a table solution or having the outer instance available too is perfectly adequate in ABAP, which is why I’m still searching for a good use case 🙂

      /
      🙂
      • I’m sorry for being so critical, but still feel like I should answer: Shared mutable state, even though you reduced the scope here, can be quite difficult to reason about.

        I’d model this using two domain objects D1 and D2 representing the items and passing those explicitly to the instances of C:

        C1=NEW C(D1)

        C2=NEW C(D1)

        C3=NEW C(D2)

        C4=NEW C(D2)

        The add() methods belongs to the domain class D. It is clear from the code in C that the data is contained in and owned by D. There can be loose coupling between C and D (C is not responsible to create instances of D, so an interface would sufice), allowing D to be extended without changing C.

        You could still have a factory P to create C and D. But after that the factory can actually be garbage collected because there is no reference pointing to it anymore. The data is contained in the domain objects D1 and D2.

        • No worries, I appreciate your input! I wrote this mainly for discussion as I see no practical use yet.

          You are absolutely right that this can be better modelled. In your example however we have both classes back in play, just their relationships switched around. Perfectly valid, and one possibility on how I’d do this in real life.

          But, what if we don’t want the domain object around? With dynamic techniques I could start executing methods of D once I have the instance of C. I admit this is a bit of a made up example but not entirely unrealistic.

          Incidentally one of the triggers for this blog was also the Restful ABAP course on openSAP, where SAP are doing something equally weird of putting their RAP classes in local classes of a global abstract class (which dumps in normal ABAP), just so that developers cannot use the classes from outside.

          I don’t think I would ever use this in real world but I do like to explore and discuss ideas. Any (minor) benefits of this setup are almost certain to be negated by the obscurity it creates for the poor soul who has to figure it out later on.

      • Well, it’s a matter of what a “closure” is, maybe the name is abusive when it’s about ABAP. I didn’t talk about “static attributes”, it was just a mention of SAP restriction about “STATICS” key word in instance methods. I was more talking about the original meaning of “closure/private”, which is not the same as private in ABAP classes. In ABAP, it seems to be like an instance attribute, but which can be used only inside the method, not in other methods of the same class.

        I am starting to see what you are trying to achieve with your example of Parents and Children. I have difficulties to understand how it’s related to a closure. I’m confused globally in fact. For instance, I wonder why you are talking about garbage collection, I consider it’s normal that it doesn’t free the instances and the counter.

        I think that with a real use case, I would better understand 🙂

        EDIT: I’ve just read all the recent comments. I think I need to read more in depth your 2 blog posts, that’s one level above my current knowledge.

        /
        🙂
  • Sorry, I don’t see a closure here: num1 and num2 are both mutable objects, generated by a factory method get_num(). Then you call add() on the mutable objects, which consequently change their state. The fact that you use references to integers doesn’t make difference: It is just a different kind of object, and the garbage collector doesn’t remove it as long as it can be reached.

    For a closure you need nested lexical scope and functions with free variables. Then the free variables of the functions are bound to the outer scope.

    It’s a long time since I wrote ABAP, but I don’t think it has the ingredients for this, e. g. nested scopes and first class functions or at least method references disguised as lambdas as in Java.

    • I agree that it is not a ‘pure’ closure. ABAP doesn’t have closures built in, and this was my experiment of whether we can implement a closure-like behaviour.

      ABAP doesn’t have true nested functions, so I’ve had to substitute this with a kind of composition relationship. Though ABAP is lexically scoped, it’s not relevant here in ABAP since we don’t really have true nested objects. Even a local class within a global is more of a visibility construct rather than a ‘contains’ relationship.

      So where a closure relies on lexical scoping I had to substitute the implicit pointer of other languages with an explicit reference. But other than having to do it explicitly, it still happens at the same point in time and in the end behaves the same as a closure with respect to the accessibility, mutability and lifetime of the data.

      • My question is: What problem do you want to solve? Closures in functional languages like Scheme can be useful to implement something like objects with mutable state.

        Often though closures, especially in JavaScript code, lead to code smells. confusion and “callback hell”. They are used as a kind of implicit passing of parameters. This could be implemented better by explicit passing of parameters, partial application of a function or instance variables of an object (such as you did).

         

        • As I explained in the blog, I am still looking for a problem to solve 🙂

          It was a crazy idea to see if it could be done, no other reason. I figured that depending on how it works out, I may discover some use for it or at the very least learn something about ABAP. So far it’s just been the latter, an interesting journey of discovery, nothing more. Yet.

          I did find it fascinating to discover how instance attribute’s lifetimes are managed beyond the objects lifetime, which made the closure-like behaviour possible and was my motivation for writing this. And specifically because of the rather experimental and possibly pointless nature of doing this in ABAP, I wrote about closures in a separate blog to the memory handling part.

          /
          🙂
          • My suspicion is that internally both instances of lcl_main are kept alive, because of the reference to the instance attribute. If the class would hold a lot of other data, e. g. selected from the database, this could have a very negative impact on memory consumption: You think that the instance should be garbage collected but because of the reference to one of the instance attributes it is still reachable.

          • Thank you, I didn’t know about memory inspector. With that you should be able to tell apart the internal implementation of ABAP and whether they actually keep just the attribute alive or the whole object.

  • Hello Mike,

    you have there an interesting method to capture the variable and pass it to the routine. IMHO this simulates a closure only in simple cases, e.g. where the logic is completely implemented within the ADD( ) method without further subroutines calls.

    Technically, a closure is a simple structure with the subroutine and the environment.  An environment maps variable names to their values. In a subroutine, the scope changes from the global to the local environment of the routine. A variable name could be bound to different values in both envirnoments. In the following code, the value of variable Y is 2 or 4, depending on the environment:

    DATA x TYPE i VALUE 1.
    DATA y TYPE i VALUE 2.
    
      PERFORM sum.
    
      PERFORM local.
      x = 6.
      PERFORM local.
    
    FORM local.
      DATA y TYPE i VALUE 4.
      PERFORM sum.
    ENDFORM.
    
    FORM sum.
      DATA z TYPE i.
    
      z = x + y.
      WRITE:/ z.
    ENDFORM.

    The output will be 3, 3, 8 since the global environment is always used in the ABAP call of subroutine SUM.

    The output 3,5,10 would be expected if we could create a closure with the local environment of routine LOCAL as it was used to call routine SUM.

    The workaround we can use is to add Y as a parameter to the SUM subroutine. I see your technique as another method to pass the variable value from another environment. The limitation is that we really need the whole environment to be available and it should be passed as well with further calls to  other subroutines.

    So IMHO closures are a simple modularization technique we cannot use them in ABAP, but we have a workaround that usually involves global variables.

    my 2 cents

    JNN

    • Hi Jaques,

      Interesting comment. But aren’t we both explaining the same?

      Lexical scoping means the environment a variable is defined at compile time in determines it’s scope, as opposed to the environment it’s called from. Your example explains this nicely and demonstrates the lexically scoped nature of ABAP, a dynamically scoped language would result in 3, 5, 8.

      You are also correct that this is more of a simulation, my example has nothing to do with scoping, as Christian Schaefer has also pointed out. I must explicitly pass the reference into the inner function in order to make it happen. The main closure-like behaviour is that we don’t need the outer function anymore, even though it is the ‘keeper of the data’.

      Another key difference with ‘real’ closures is that no execution of the inner function takes place during the initial call, as opposed to my object instantiation. The inner function is simply passed back as a complete ‘ready to run’ piece of code and data reference, and the caller can do with it as it pleases and needn’t care where the value comes from.

      The need for this does’t really exist in ABAP, so this is mostly academic, but I’ve found these discussions interesting, thanks.

        • An excellent justification! 🙂

          I played around with this around 2 years ago and put it aside as a solution seeking a problem. My quest is over!

          Incidentally, since you’re on the RAP course too, it was their weird non-conformist setup of writing stuff in an otherwise inaccessible local class that reminded me of my closure experiment and decide to blog about it.

          I tried to figure out how they did it, but just ended up in dumps. I found the answer: If you’re SAP you can break the rules and do what you like:

          method INVOKE
            by kernel module ab_km_behv_handler_invoke
                             fail.
          endmethod.