Well, at least in ABAP classes.

Default Parameter Values

Different programming languages have different implementations for default parameter values. That is, parameters that can be supplied but need not be.

Erlang

For example, Erlang does not have default parameter values at all: function timer:send_after/3 must always be called with three parameters.

1
2
-spec send_after(Time, Destination, Message) ->
    {'ok', TRef} | {'error', Reason}.

However, since it is common for a process to queue a message to itself, it would be handy to have a shortcut for “send message to me after some time”. One option for this (in other languages) would be to use the current process as the default value of parameter Destination. Erlang, which doesn’t have default parameter values, implements exactly the same using overloading:

1
2
send_after(Time, Message) ->
    send_after(Time, self(), Message).

Because timer:send_after/2 is not the same as timer:send_after/3, no default parameters are necessary. Calling timer:send_after/2 becomes a call to timer:send_after/3 with the “default parameter value” in place.

A simpler example would go like this:

1
2
3
4
5
hello(Name) ->
    "Hello, " ++ Name ++ "!".
    
hello() ->
    hello("world").

Swift

In Swift, parameters can have default values. Both positional and labeled parameters can have default values, but default values are a more natural fit for labeled parameters.

1
2
3
4
5
6
7
8
9
func hello(to name: String = "world") -> String {
    "Hello, \(name)!"
}

hello(to: "you")
// "Hello, you!"

hello()
// "Hello, world!"

Scala

Scala has both default values and also another type of optional parameter, implicit parameters.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
object Hello extends App {
    def helloDefault(name: String = "world"): String = s"Hello, $name!\n"
    
    print(helloDefault())
    // Hello, world!
    
    print(helloDefault("you"))
    // Hello, you!
    
    
    type ImplicitName = String
    implicit val implicitName: ImplicitName = "world"
    
    def helloImplicit()(implicit name: ImplicitName): String = s"Hello, $name!\n"
    
    print(helloImplicit())
    // Hello, world!
    
    print(helloImplicit()("you"))
    // Hello, you!
}

JavaScript

In JavaScript, all parameters are (technically) optional and have undefined as the default value.

1
2
3
4
5
6
7
function hello() {
    if (typeof arguments[0] === "string") {
        return "Hello, " + arguments[0];
    } else {
        return "Hello, world!";
    }
}

Others

Java uses overloading, like Erlang.

Go doesn’t support overloading or default parameter values.

C# has default parameter values, like Swift and Scala.

Python has default parameter values, like Swift, Scala and C#.

Default and Optional Parameters in ABAP

ABAP, unlike many other languages, has both OPTIONAL and DEFAULT parameters.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
CLASS class DEFINITION.

  PUBLIC SECTION.
    METHODS method1
      IMPORTING im_name TYPE string OPTIONAL
      RETURNING VALUE(re_result) TYPE string.
  
    METHODS method2
      IMPORTING im_name TYPE string DEFAULT 'world'
      RETURNING VALUE(re_result) TYPE string.

ENDCLASS.

CLASS class IMPLEMENTATION.

  METHOD method1.
    re_result = |Hello, { COND #( WHEN im_name IS NOT INITIAL THEN im_name ELSE 'world' ) }!|.
  ENDMETHOD.
  
  METHOD method2.
    re_result = |Hello, { im_name }!|.
  ENDMETHOD.

ENDCLASS.

OPTIONAL and DEFAULT are not really different things, OPTIONAL just uses the type-specific INITIAL value as the value of the parameter (empty string for character fields, 0 for integers and so forth).

Apart from the fact that there are two options for doing basically the same thing, this is pretty much like in other languages.

IS SUPPLIED…

What all the languages listed above – excluding ABAP – have in common, is that the methods cannot tell if they were called with an explicit value passed to the parameter where a default value is also an option. A parameter could obviously have a value that the method will then recognize as the default value, but even then someone could call the method by providing that default value to that parameter, and the method would be none the wiser.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
func hello(to name: String = "world") -> String {
    guard name == "world" else {
        // non-default value
        return "Hello, \(name)! (not default)"
    }
    
    // default value
    return "Hello, \(name)!"
}

// The implementation of the "hello" function cannot tell
// the difference between these two calls

hello()
// "Hello, world!"

hello(to: "world")
// "Hello, world!"

In ABAP, it is possible to know if a value was passed to an optional parameter: enter IS SUPPLIED.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
CLASS class DEFINITION.
  PUBLIC SECTION.
    METHODS method
      IMPORTING im_parameter TYPE string DEFAULT 'world'.
ENDCLASS.

CLASS class IMPLEMENTATION.

  METHOD method.

    IF im_parameter IS SUPPLIED AND im_parameter = 'world'.
      " Method was invoked as class->method( im_parameter = 'world' ).
    ELSEIF im_parameter IS SUPPLIED.
      " Method was invoked as class->method( im_parameter = 'other value' ).
    ELSEIF im_parameter = 'world'.
      " Method was invoked as class->method( ).
    ENDIF.

  ENDMETHOD.
  
ENDCLASS.

Often the first reaction of programmers is “Cool!”, since IS SUPPLIED can obviously distinguish between an initial value and a value that was not supplied at all. I would, however, argue that IS SUPPLIED is more trouble than its worth and ABAPpers would do well by making do with only the features that other languages have.

…Considered Harmful

The trouble with IS SUPPLIED arises when it is combined with classes. Consider the following:

 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
CLASS delivery_item DEFINITION.

  PUBLIC SECTION.
    METHODS set_foreign_trade_countries
      IMPORTING im_country_of_origin TYPE herkl OPTIONAL
                im_dispatch_country  TYPE verld OPTIONAL.
    METHODS save.

ENDCLASS.

CLASS delivery_item IMPLEMENTATION.

  METHOD set_foreign_trade_countries.

    IF im_country_of_origin IS SUPPLIED.
      country_of_origin = im_country_of_origin.
      update_country_of_origin = abap_true.
    ENDIF.

    IF im_dispatch_country IS SUPPLIED.
      dispatch_country = im_dispatch_country.
      update_dispatch_country = abap_true.
    ENDIF.

  ENDMETHOD.

ENDCLASS.

Method set_foreign_trade_countries allows caller to set country of origin, dispatch country or both, depending on how the method is invoked. This doesn’t look too bad and having the option to only partially update foreign trade details seems like a handy feature.

Now we subclass the delivery item.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
CLASS delivery_item_germany DEFINITION INHERITING FROM delivery_item.

  PUBLIC SECTION.
    METHODS set_foreign_trade_countries REDEFINITION.

ENDCLASS.

CLASS delivery_item_germany IMPLEMENTATION.

  METHOD set_foreign_trade_countries.

    super->set_foreign_trade_countries( im_country_of_origin = im_country_of_origin
                                        im_dispatch_country  = im_dispatch_country ).

    update_additional_fields( ).

  ENDMETHOD.

ENDCLASS.

This is more or less how we would implement the method in the subclass. However, in ABAP this naive implementation will not work.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
DATA(lo_delivery_item_germany) = NEW delivery_item_germany( ).

" Clears dispatch country unintentionally
lo_delivery_item_germany->set_foreign_trade_countries( im_country_of_origin = 'FI' ).

" Clears country of origin unintentionally
lo_delivery_item_germany->set_foreign_trade_countries( im_dispatch_country = 'DE' ).

" Having to specify both loses the convenience
" we were going for with IS SUPPLIED and is
" not enforced by the compiler
lo_delivery_item_germany->set_foreign_trade_countries( im_country_of_origin = 'FI'
                                                       im_dispatch_country  = 'DE' ).

Because the superclass method can detect how it is called, the exact form of the method call has to pass unchanged all the way through the class hierarchy or otherwise IS SUPPLIED will not work as intended:

 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
CLASS delivery_item_germany DEFINITION INHERITING FROM delivery_item.

  PUBLIC SECTION.
    METHODS set_foreign_trade_countries REDEFINITION.

ENDCLASS.

CLASS delivery_item_germany IMPLEMENTATION.

  METHOD set_foreign_trade_countries.

    IF im_country_of_origin IS SUPPLIED AND im_dispatch_country IS SUPPLIED.
      super->set_foreign_trade_countries( im_country_of_origin = im_country_of_origin
                                          im_dispatch_country  = im_dispatch_country ).
    ELSEIF im_country_of_origin IS SUPPLIED.
      super->set_foreign_trade_countries( im_country_of_origin = im_country_of_origin ).
    ELSEIF im_dispatch_country IS SUPPLIED.
      super->set_foreign_trade_countries( im_dispatch_country = im_dispatch_country ).
    ELSE.
      super->set_foreign_trade_countries( ).
    ENDIF.

    update_additional_fields( ).

  ENDMETHOD.

ENDCLASS.

One optional parameter requires two method call combinations in every subclass, two optional parameters will require four and so forth. Not only is this a great place for hard-to-detect bugs, it will also add unnecessary hits to your where-used lists.

So using IS SUPPLIED makes inheriting normal methods arduous, but it has an even worse effect on special methods like constructor:

 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
CLASS class DEFINITION.

  PUBLIC SECTION.
    METHODS constructor
      IMPORTING im_description TYPE string OPTIONAL.
  PRIVATE SECTION.
    DATA description TYPE string.

ENDCLASS.

CLASS class IMPLEMENTATION.

  METHOD constructor.

    IF im_description IS SUPPLIED.
      description = im_description.
    ELSE.
      description = 'Default Description'.
    ENDIF.

  ENDMETHOD.

ENDCLASS.


CLASS subclass DEFINITION INHERITING FROM class.

  PUBLIC SECTION.
    METHODS constructor
      IMPORTING im_description TYPE string OPTIONAL
                im_other_field TYPE string.
  PRIVATE SECTION.
    DATA other_field TYPE string.

ENDCLASS.

CLASS subclass IMPLEMENTATION.

  METHOD constructor.

    IF im_description IS SUPPLIED.
      super->constructor( im_description = im_description ).
    ELSE.
      super->constructor( ).
    ENDIF.


    other_field = im_other_field.

  ENDMETHOD.

ENDCLASS.

Trying the compile this will produce the following error:

constructor call cannot be conditional

In other words, it is impossible to cover all possible combinations of IS SUPPLIED logic in constructors.

So IS SUPPLIED can cause issues in methods, but it should be fine in function modules, right? Well, if you think back on how many BAPI function modules are basically just wrappers over other function modules, with minimal to no changes to the parameters, it would appear that the same troublesome pattern arises with function modules as well. There are even cases where IS SUPPLIED doesn't work with function modules. So you’d probably be better off by giving up IS SUPPLIED.

What about IS SUPPLIED with CHANGING parameters? You’d probably be better off by making the CHANGING parameter mandatory if it is really required. If you just want to return something to the caller, use RETURNING or EXPORTING.

What about IS SUPPLIED with EXPORTING parameters? You’d probably be better off by using RETURNING or making the EXPORTING parameters VALUE()s in order to uncouple the method implementation from how it was technically called.

Conclusion

IS SUPPLIED introduces undesirable and unconventional coupling between the caller and method implementation. This coupling makes subclassing and other methods of wrapping existing functionality subject to strange bugs and cumbersome implementations. Other programming languages make do fine without such a programming language structure and ABAPpers would be wise to follow their example and avoid IS SUPPLIED.