Intermediate

Functions in ABAP

Learn how ABAP packages reusable logic - classic FORM subroutines with PERFORM, ABAP Objects methods with IMPORTING/RETURNING parameters, default parameters, scope, and recursion

Reusable units of logic are the backbone of any non-trivial program, and ABAP - a multi-paradigm language that grew from procedural roots in 1983 into full object orientation in 1999 - offers more than one way to package them. Because ABAP spans both worlds, “functions” in ABAP is really three related ideas: classic subroutines (FORM / PERFORM), reusable function modules that live in function groups, and methods on classes from ABAP Objects.

This tutorial focuses on the two forms you can write and run in a single standalone program: the classic procedural subroutine and the modern object-oriented method. Subroutines (FORM ... ENDFORM) are the legacy workhorse - still everywhere in older SAP code - and pass data through USING and CHANGING parameter lists. Methods are the modern approach: they declare typed IMPORTING, EXPORTING, CHANGING, and RETURNING parameters, support default values, and - when they use RETURNING - can be called inline like a function in any expression.

In this tutorial you’ll see both styles in one runnable program: instance and static methods, a RETURNING parameter that enables function-style calls, a default parameter, recursion (the classic factorial), and how local variables scope inside a routine. The example runs on Node.js through the open-abap transpiler, so no SAP system is required.

Subroutines: FORM and PERFORM

A subroutine is defined with FORM name ... ENDFORM and invoked with PERFORM name. Parameters are grouped by intent:

  • USING - inputs passed into the subroutine.
  • CHANGING - values the subroutine reads and writes back to the caller (the idiomatic way a FORM “returns” a result).

By convention, FORM routines sit at the end of a report, after the event blocks. They are considered obsolete in modern (clean-core) ABAP, but you will encounter millions of lines of them in real systems, so they are essential to recognize.

Methods: ABAP Objects

A class declares methods in its DEFINITION and codes them in its IMPLEMENTATION. Method parameters are explicitly typed and categorized:

  • IMPORTING - inputs (can specify DEFAULT values to make them optional).
  • EXPORTING - outputs passed back through named parameters.
  • CHANGING - read-write parameters.
  • RETURNING VALUE(...) - a single return value that turns the method into a functional method you can call inline, e.g. DATA(x) = obj->add( ... ).

Methods come in two flavors: instance methods (METHODS), called on an object with ->, and static methods (CLASS-METHODS), called on the class itself with =>. Recursion is natural - a method can simply call itself.

Putting It All Together

The program below defines a small local class lcl_math with an instance method (add), a method with a default parameter (power), and a static recursive method (factorial). It then demonstrates two classic FORM subroutines, including local variable scope.

Create a file named functions.abap:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
REPORT zfunctions.

" ============================================================
" Local class: methods (ABAP Objects)
" ============================================================
CLASS lcl_math DEFINITION.
  PUBLIC SECTION.
    " Instance method, RETURNING enables function-style calls
    METHODS add
      IMPORTING iv_a          TYPE i
                iv_b          TYPE i
      RETURNING VALUE(rv_sum) TYPE i.

    " Default parameter: iv_exp is optional, defaults to 2
    METHODS power
      IMPORTING iv_base          TYPE i
                iv_exp           TYPE i DEFAULT 2
      RETURNING VALUE(rv_result) TYPE i.

    " Static method using recursion
    CLASS-METHODS factorial
      IMPORTING iv_n             TYPE i
      RETURNING VALUE(rv_result) TYPE i.
ENDCLASS.

CLASS lcl_math IMPLEMENTATION.
  METHOD add.
    rv_sum = iv_a + iv_b.
  ENDMETHOD.

  METHOD power.
    rv_result = 1.
    DO iv_exp TIMES.
      rv_result = rv_result * iv_base.
    ENDDO.
  ENDMETHOD.

  METHOD factorial.
    " n! = n * (n-1)!  - the method calls itself
    IF iv_n <= 1.
      rv_result = 1.
    ELSE.
      rv_result = iv_n * factorial( iv_n - 1 ).
    ENDIF.
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.

  DATA(lo_math) = NEW lcl_math( ).

  " Instance method called like a function (RETURNING)
  DATA(lv_sum) = lo_math->add( iv_a = 4 iv_b = 9 ).
  WRITE |4 + 9 = { lv_sum }|.

  " Default parameter: iv_exp omitted, so it defaults to 2
  DATA(lv_sq) = lo_math->power( iv_base = 5 ).
  WRITE / |5 squared = { lv_sq }|.

  " Default parameter overridden
  DATA(lv_cube) = lo_math->power( iv_base = 2 iv_exp = 3 ).
  WRITE / |2 cubed = { lv_cube }|.

  " Static method (=>) using recursion
  DATA(lv_fact) = lcl_math=>factorial( 5 ).
  WRITE / |5! = { lv_fact }|.

  " Classic subroutine: PERFORM ... USING ... CHANGING
  DATA lv_area TYPE i.
  PERFORM rectangle_area USING 6 7 CHANGING lv_area.
  WRITE / |Area of 6 x 7 = { lv_area }|.

  " Subroutine with input parameters only
  PERFORM show_label USING `Total` 42.

" ============================================================
" Classic subroutines (FORM ... ENDFORM) live at the end
" ============================================================
FORM rectangle_area USING    p_width  TYPE i
                             p_height TYPE i
                    CHANGING p_area   TYPE i.
  " p_local is local: visible only inside this subroutine
  DATA p_local TYPE i.
  p_local = p_width * p_height.
  p_area  = p_local.
ENDFORM.

FORM show_label USING p_text  TYPE string
                      p_value TYPE i.
  WRITE / |{ p_text }: { p_value }|.
ENDFORM.

Running with Docker

Run the program using the same open-abap transpiler pattern from the Hello World tutorial - this time pointing at functions.abap instead of hello.abap.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# Pull the Node.js image
docker pull node:20

# Run the ABAP program
docker run --rm -v $(pwd):/work node:20 sh -c '
cd /work && mkdir -p abap_project/src && cd abap_project && \
npm init -y >/dev/null 2>&1 && \
npm install --silent @abaplint/transpiler-cli @abaplint/runtime 2>/dev/null && \
echo "{\"input_folder\":\"src\",\"output_folder\":\"output\",\"write_unit_tests\":false,\"options\":{\"ignoreSyntaxCheck\":true}}" > abap_transpile.json && \
echo "{\"global\":{\"files\":\"/src/**/*.*\"},\"syntax\":{\"version\":\"v702\",\"errorNamespace\":\".\"}}" > abaplint.json && \
cp /work/functions.abap src/zfunctions.prog.abap && \
echo "<?xml version=\"1.0\"?><abapGit version=\"v1.0.0\"><asx:abap xmlns:asx=\"http://www.sap.com/abapxml\"><asx:values><PROGDIR><NAME>ZFUNCTIONS</NAME><SUBC>1</SUBC></PROGDIR></asx:values></asx:abap></abapGit>" > src/zfunctions.prog.xml && \
./node_modules/.bin/abap_transpile >/dev/null && \
echo "import runtime from \"@abaplint/runtime\";globalThis.abap = new runtime.ABAP();await import(\"./output/zfunctions.prog.mjs\");" > run.mjs && \
node run.mjs && cd /work && rm -rf abap_project'

The transpiler converts the ABAP source to JavaScript, then @abaplint/runtime executes it - reproducing ABAP’s list buffer output on stdout.

Expected Output

4 + 9 = 13
5 squared = 25
2 cubed = 8
5! = 120
Area of 6 x 7 = 42
Total: 42

Notes on Behavior

A few details worth calling out:

  • The first WRITE has no leading /, so it lands on the first line of the list. Every subsequent WRITE / starts a new line - the same list-buffer behavior introduced in Hello World.
  • RETURNING is what makes a method functional: because add, power, and factorial each return a single value, they can be called inline inside an expression (DATA(lv_sum) = lo_math->add( ... )). A method with only EXPORTING parameters would instead require the longer CALL METHOD ... IMPORTING ev_x = ... form.
  • Instance methods are called with -> on an object reference; static (CLASS-METHODS) methods are called with => on the class name itself.
  • iv_exp TYPE i DEFAULT 2 makes that parameter optional. Calling power( iv_base = 5 ) uses the default 2, yielding 25; passing iv_exp = 3 overrides it.
  • Inside rectangle_area, p_local is a local variable - it exists only for the duration of that subroutine call and is not visible to the caller or to other routines. Data declared at the top level of the report (like lv_area) is effectively global to the program.
  • FORM subroutines return results through CHANGING parameters - PERFORM rectangle_area ... CHANGING lv_area writes the computed area back into lv_area.

Real-World ABAP: Function Modules and Methods

Beyond subroutines and class methods, classic SAP systems lean heavily on function modules - globally reusable routines stored in function groups and maintained in transaction SE37. They cannot be defined inline in a single report (they require a function group), but their signature uses the same parameter categories:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
FUNCTION z_calculate_tax.
*"----------------------------------------------------------------------
*"  IMPORTING
*"     VALUE(IV_AMOUNT) TYPE  P
*"     VALUE(IV_RATE)   TYPE  P DEFAULT '0.20'
*"  EXPORTING
*"     VALUE(EV_TAX)    TYPE  P
*"----------------------------------------------------------------------
  ev_tax = iv_amount * iv_rate.
ENDFUNCTION.

Such a function module is invoked with CALL FUNCTION:

1
2
3
4
5
CALL FUNCTION 'Z_CALCULATE_TAX'
  EXPORTING
    iv_amount = lv_amount
  IMPORTING
    ev_tax    = lv_tax.

In modern, clean-core ABAP the recommendation is to prefer class methods (as shown in the runnable example) over both FORM subroutines and new function modules, reserving function modules for remote-enabled calls (RFC) and legacy integration points.

Key Concepts

  • FORM ... ENDFORM / PERFORM - classic procedural subroutines; pass inputs with USING and return results through CHANGING parameters.
  • METHODS vs CLASS-METHODS - instance methods called with -> on an object, static methods called with => on the class.
  • Parameter categories - IMPORTING (in), EXPORTING (out), CHANGING (in/out), and RETURNING VALUE(...) (single return value).
  • RETURNING enables functional calls - a method with a RETURNING parameter can be used inline in any expression, just like a function.
  • Default parameters - IMPORTING iv_exp TYPE i DEFAULT 2 makes an argument optional with a fallback value.
  • Recursion - methods (and subroutines) can call themselves; the factorial example follows n! = n * (n-1)!.
  • Scope - variables declared inside a FORM or METHOD are local to that routine; top-level report data acts as global state.
  • Function modules - globally reusable routines in function groups, called with CALL FUNCTION; prefer class methods in modern ABAP.

Running Today

All examples can be run using Docker:

docker pull node:20
Last updated:

Comments

Loading comments...

Leave a Comment

2000 characters remaining