Functional Programming in ABAP Series: PART I (Introduction)
Background
In the intricate world of software development, the quality of our code is paramount. This quality impacts not only the performance of our programs but also the efficiency and effectiveness of developers. Defining high-quality code proves challenging, with nuances extending beyond mere execution speed.
High-quality code embodies characteristics such as testability, maintainability, reusability, and extensibility. These metrics form the yardstick for evaluating code robustness. However, the path to achieving these qualities is often ambiguous, prompting exploration of methodologies that can guide developers toward writing code that stands the test of time.
One key approach is rooted in functional programming principles. Functional programming, a paradigm inspired by mathematical functions, emphasizes expressions and the composition of functions. Separating pure functions, devoid of side effects, from impure ones lays the foundation for code that is not only clear and concise but also facilitates testing and maintenance.
In the context of ABAP, Object-Oriented ABAP emerges as a natural conduit for implementing functional programming principles. While not a one-size-fits-all solution, functional programming enriches our problem-solving perspectives. This blog seeks to demonstrate the application of these principles in Object-Oriented ABAP, illustrating how they can elevate code quality and resilience.
Functional Programming in A Nutshell
Functional programming, in essence, revolves around the paradigm of programming using functions. It is a distinctive approach that leverages the principles of higher-order functions, rooted in mathematical functions as its foundational model.
At its core, functional programming relies on functions inspired by mathematical functions. The hallmark of this paradigm lies in the composition of expressions, where programs are a fusion of concrete values, variables, and functions. Functions, within the context of functional programming, hold a specific definition — they are expressions that, when applied to an argument or input, can be subsequently reduced or evaluated.
This approach contrasts with imperative programming paradigms, emphasizing a declarative style that promotes immutability, referential transparency, and the avoidance of side effects. Functional programming fosters a modular and composable design, allowing developers to create robust, maintainable, and scalable codebases.
Let’s dissect the provided Haskell code examples, showcasing the essence of functional programming:
even :: (Integral a) => a -> Bool
even x
| x `mod` 2 == 0 = True
| otherwise = False
Here, we define a function even that takes an integral value x and returns a boolean indicating whether x is even. The use of guards (|) makes the code concise and expressive. This function demonstrates the functional style by focusing on the computation rather than detailing step-by-step instructions.
odd :: (Integral a) => a -> Bool
odd = not . even
In this example, we leverage the composition operator (.) to define the odd function succinctly. It takes advantage of the previously defined even function, showcasing the power of composing functions to create new ones.
filter :: (a -> Bool) -> [a] -> [a]
filter _ [] = []
filter pred (x:xs)
| pred x = x : filter pred xs
| otherwise = filter pred xs
The filter function demonstrates a common higher-order function in functional programming. It takes a predicate function pred and a list of any type [a], returning a new list containing only the elements that satisfy the predicate. This is a clear example of functional programming’s emphasis on using higher-order functions for concise and expressive code. In this function, we showcase the recursive technique — a fundamental aspect of functional programming. Recursion is not only a technical approach but also a distinctive way of thinking. Embracing recursion allows us to break down complex problems into simpler, more manageable sub-problems, fostering code elegance and a deeper understanding of functional programming principles.
ghci> filter odd [1..100]
[1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,33,35,37,39,41,43,45,47,49,51,53,55,57,59,61,63,65,67,69,71,73,75,77,79,81,83,85,87,89,91,93,95,97,99]
ghci> filter even [1..100]
[2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98,100]
These examples demonstrate Haskell’s functional programming features, emphasizing the concise and declarative nature of the code. The use of higher-order functions, immutability, and function composition contributes to the readability and maintainability of the codebase. These principles form the basis for our exploration of functional programming in Object-Oriented ABAP in the subsequent sections.
Translating Functional Programming Concepts: Haskell to ABAP
Let’s seamlessly translate the Haskell code above into ABAP, aiming for a natural adaptation that captures the essence of functional programming concepts applied in an Object-Oriented (OO) paradigm, specifically within the ABAP language.
Here is the code implementation,
ABAP
INTERFACE zif_predicate
PUBLIC .
METHODS evaluate IMPORTING value TYPE any
RETURNING VALUE(result) TYPE abap_bool.
ENDINTERFACE.
The zif_predicate interface serves as a contract or blueprint for classes that implement a specific method signature, in this case, the evaluate method. It defines a common contract that the zcl_predicate_even and zcl_predicate_odd classes adhere to.
- Purpose: To declare a method evaluate that takes a value and returns a boolean result.
- Usage: All classes that implement this interface are guaranteed to adhere to a consistent contract. In this context, the interface is utilized to implement a higher-order function through the application of dependency injection.
Even Predicate in Haskell vs ABAP
Haskell
even :: (Integral a) => a -> Bool
even x
| x `mod` 2 == 0 = True
| otherwise = False
ABAP
CLASS zcl_predicate_even DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
INTERFACES zif_predicate.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS zcl_predicate_even IMPLEMENTATION.
METHOD zif_predicate~evaluate.
FIELD-SYMBOLS <value> TYPE i.
ASSIGN value TO <value>.
result = COND #( WHEN <value> MOD 2 = 0
THEN abap_true
ELSE abap_false ).
ENDMETHOD.
ENDCLASS.
In both languages, we define a predicate to check if a number is even. The ABAP implementation uses an interface zif_predicate to establish a common contract, and the evaluate method mirrors the Haskell logic. In addition, we exemplify the application of dynamic programming in ABAP to adeptly handle generic data types which is common in functional programming world.
Odd Predicate in Haskell vs ABAP
Haskell
odd :: (Integral a) => a -> Bool
odd = not . even
ABAP
CLASS zcl_predicate_odd DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
INTERFACES zif_predicate.
PROTECTED SECTION.
PRIVATE SECTION.
METHODS negate IMPORTING truth TYPE abap_bool
RETURNING VALUE(result) TYPE abap_bool.
ENDCLASS.
CLASS zcl_predicate_odd IMPLEMENTATION.
METHOD zif_predicate~evaluate.
result = negate(
CAST zif_predicate(
NEW zcl_predicate_even( )
)->evaluate( value = value ) ).
ENDMETHOD.
METHOD negate.
result = COND #( WHEN truth = abap_true
THEN abap_false
ELSE abap_true ).
ENDMETHOD.
ENDCLASS.
Here, the odd predicate in Haskell is defined by negating the even predicate. In ABAP, we encapsulate this logic in the zcl_predicate_odd class, which internally leverages the zcl_predicate_even class to evaluate the evenness. in this part, we use the function composition in both languages. In functional programming, function composition is the key of handling complexity.
Filtering Elements in a List in Haskell vs ABAP
Haskell
filter :: (a -> Bool) -> [a] -> [a]
filter _ [] = []
filter pred (x:xs)
| pred x = x : filter pred xs
| otherwise = filter pred xs
ABAP
CLASS zcl_filter DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
METHODS get_filtered IMPORTING predicate TYPE REF TO zif_predicate
value_tab TYPE ANY TABLE
RETURNING VALUE(result) TYPE REF TO data.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS zcl_filter IMPLEMENTATION.
METHOD get_filtered.
FIELD-SYMBOLS <result_tab> LIKE value_tab.
CREATE DATA result LIKE value_tab.
ASSIGN result->* TO <result_tab>.
LOOP AT value_tab ASSIGNING FIELD-SYMBOL(<value>).
IF predicate->evaluate( <value> ) = abap_false.
CONTINUE.
ELSE.
INSERT <value> INTO TABLE <result_tab>.
ENDIF.
ENDLOOP.
ENDMETHOD.
ENDCLASS.
In both languages, the filter function is implemented as a higher-order function. In Haskell, it operates on lists, and in ABAP, it uses a generic table structure. The ABAP implementation defines a class zcl_filter that takes a predicate and a table of values, returning a filtered result. In imperative programming languages including ABAP, we prefer to use loop over recursive method, always. and also the handling of generic data here becomes more complex but still acceptable. In this method, we observe the utilization of higher-order functions in ABAP through the implementation of dependency injection. While in functional programming, functions are considered first-class citizens, in Object-Oriented Programming (OOP), objects take on this primary role.
Usage in GHCi vs ABAP Demo
ABAP
CLASS zcl_demo_fp DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS zcl_demo_fp IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
TYPES ty_t_integer TYPE STANDARD TABLE OF i WITH EMPTY KEY.
DATA(filter) = NEW zcl_filter( ).
DATA(even) = CAST zif_predicate( NEW zcl_predicate_even( ) ).
DATA(odd) = CAST zif_predicate( NEW zcl_predicate_odd( ) ).
DATA value_tab TYPE ty_t_integer.
" get even number from 1 to 100
out->write( `Even Numbers` ).
DATA(even_number) = filter->get_filtered(
predicate = even
value_tab = VALUE ty_t_integer( FOR i = 1 THEN i + 1 UNTIL i = 100
( i ) )
).
ASSIGN even_number->* TO FIELD-SYMBOL(<even_number>).
IF <even_number> IS ASSIGNED.
value_tab = CORRESPONDING #( <even_number> ).
DATA(string_even) = REDUCE string( INIT evens TYPE string
FOR <even> IN value_tab
NEXT
evens = COND #( WHEN evens IS INITIAL
THEN <even>
ELSE |{ evens }, { <even> }| ) ).
out->write(
EXPORTING
data = |[{ string_even }]| ).
ENDIF.
" get odd number from 1 to 100
out->write( `Odd Numbers` ).
DATA(odd_number) = filter->get_filtered(
predicate = odd
value_tab = VALUE ty_t_integer( FOR i = 1 THEN i + 1 UNTIL i = 100
( i ) )
).
ASSIGN odd_number->* TO FIELD-SYMBOL(<odd_number>).
IF <odd_number> IS ASSIGNED.
CLEAR value_tab.
value_tab = CORRESPONDING #( <odd_number> ).
DATA(string_odd) = REDUCE string( INIT odds TYPE string
FOR <odd> IN value_tab
NEXT
odds = COND #( WHEN odds IS INITIAL
THEN <odd>
ELSE |{ odds }, { <odd> }| ) ).
out->write(
EXPORTING
data = |[{ string_odd }]| ).
ENDIF.
ENDMETHOD.
ENDCLASS.
The final part of the ABAP code (zcl_demo_fp) showcases the usage of the defined classes and predicates, similar to how the examples were demonstrated in GHCi for Haskell. The demo class creates instances of the filter, even, and odd predicates, applies them to a range of integers, and outputs the results.
Overall, the ABAP code mirrors the functional programming principles demonstrated in Haskell, emphasizing the use of interfaces, encapsulation, and method-based evaluations to achieve the desired functionality in an Object-Oriented ABAP context.
Summary
In this chapter, we delved into fundamental principles of functional programming, particularly higher-order functions and function composition. Key takeaways include:
- Functional Programming Insights: We gained insights into the significance of higher-order functions and function composition as foundational principles in functional programming. Understanding these concepts is crucial for unlocking the full potential of functional programming paradigms.
- Natural Implementation in Object-Oriented Programming: Contrary to the misconception that functional programming principles are exclusive to functional languages, we demonstrated their seamless integration into Object-Oriented Programming (OOP). By leveraging dependency injection, we successfully implemented higher-order functions. Additionally, functional method composition provided a natural way to achieve function composition within an OOP framework.
Conclusion
In this chapter, we’ve embarked on a journey into the realm of functional programming concepts and their application in Object-Oriented ABAP. While the examples provided may seem relatively straightforward, they serve as crucial building blocks for enhancing our understanding of how functional programming principles can significantly impact code quality within the context of ABAP.
Key Takeaways:
- Introduction to Functional Programming in OO ABAP: We’ve uncovered how functional programming concepts, such as higher-order functions, and function composition can be seamlessly applied to the Object-Oriented ABAP. This introduction lays the foundation for elevating code quality by adopting a functional mindset.
- Learning from Trivial Examples: The examples presented may appear simple, yet they play a pivotal role in solidifying our grasp of fundamental concepts. Understanding how to tackle straightforward problems equips us with the knowledge needed to address more complex challenges in the future. A robust understanding of basics often forms the bedrock of mastering intricate concepts.
Looking Ahead:
Despite the strides made in applying functional programming principles, we recognize that this is just the tip of the iceberg. Achieving the pinnacle of high-quality code requires a more nuanced approach. Therefore, in the upcoming chapter, we’ll delve into a real-world scenario, unraveling the intricacies of design patterns influenced by functional programming concepts. By implementing design strategies grounded in functional principles, we aim to showcase the profound impact these concepts can have on crafting resilient and maintainable code.
In essence, our journey is far from complete. We’re poised to venture deeper into the intersection of functional programming and Object-Oriented ABAP, unlocking the potential for a paradigm shift in our approach to software design and development. Stay tuned as we unravel the layers of complexity and discover the transformative power of functional programming in real-world scenarios.
Reference
- Allen, C. and Moronuki, J., 2017. Haskell Programming from first principles. Gumroad, 23, p.67.
- Hutton, G., 2016. Programming in haskell. Cambridge University Press.
Contributors