20250212 ‐ functional programming - cywongg/2025 GitHub Wiki

Understanding Declarative vs. Functional Programming

Declarative Programming

Declarative programming is a style of writing code where you describe what the program should accomplish, rather than detailing how to achieve it step by step. In other words, you focus on the desired outcome. Common examples of declarative programming include SQL (you declare the desired result via queries) and markup languages like HTML (you declare structure rather than giving step-by-step commands).

Functional Programming

Functional programming is often considered a subset or specific style within declarative programming. While also focusing on what needs to be computed, it imposes a few additional constraints or principles, such as:

  1. Pure Functions: Functions that don’t cause side effects (like modifying global state) and always return the same result given the same inputs.
  2. Immutability: Data is not mutated once created. Instead, new data structures are returned.
  3. Higher-Order Functions: Functions that take other functions as arguments or return functions as their result.
  4. Referential Transparency: An expression can be replaced by its output value without changing the program’s behavior.

In this sense, functional programming is declarative with extra rules around immutability and function composition.


Checking for Null in a Functional Style

In functional programming, instead of a typical if/else statement, you often use function composition, higher-order functions, or other abstractions to handle conditional logic. Here are a couple of examples in JavaScript and Java (with Optionals) that illustrate a more functional (declarative) approach:

Example in JavaScript

// A tiny example of a "maybe" approach to handle possible null or undefined
function maybe(fn) {
  return function (value) {
    if (value == null) {
      // Value is null or undefined
      return null;
    }
    // Apply the function if value is not null
    return fn(value);
  };
}

// Example usage:
const formatUpperCase = maybe((str) => str.trim().toUpperCase());

console.log(formatUpperCase(null));      // -> null (or you could handle it differently)
console.log(formatUpperCase("  hello")); // -> "HELLO"

Here, instead of doing an if (value == null) check explicitly inside your core business logic, you can wrap it in a higher-order function (maybe). When the value is null, you simply return early. When it’s not null, it applies the function. This keeps your code more declarative because you describe the transformation rather than mixing transformation logic with condition handling.


Example in Java (with Optionals)

import java.util.Optional;

public class NullCheckExample {
    public static void main(String[] args) {
        String possiblyNullString = null;

        // Use Optional to safely operate only if non-null
        Optional.ofNullable(possiblyNullString)
            .ifPresentOrElse(
                value -> System.out.println("The value is: " + value),
                () -> System.out.println("Value was null")
            );

        // Another example of mapping only if present
        String uppercased = Optional.ofNullable(possiblyNullString)
            .map(String::toUpperCase)
            .orElse("Default Value");

        System.out.println("Uppercased (or default): " + uppercased);
    }
}

In this Java example, Optional.ofNullable(...) creates an Optional object. If the value is non-null, you operate on that value; if it’s null, it runs the orElse or ifPresentOrElse branch. This way, you don't have to explicitly write if (possiblyNullString != null) { ... }—you declare the transformation and rely on the optional or the functional functions to handle the branching.


Summary

  • Declarative programming: Emphasizes describing what you want to achieve rather than detailing how it should be done.
  • Functional programming: A style of declarative programming that imposes concepts like pure functions, immutability, and higher-order functions for safer, more predictable code.
  • Checking for null (or undefined) in a functional style often involves using higher-order functions, monads (like Maybe or Optional), or specialized abstractions to manage presence/absence of data gracefully, rather than explicit imperative if/else checks.