Arun Pandian M

Arun Pandian M

Android Dev | Full-Stack & AI Learner

Memoization: When Remembering Is Smarter Than Recomputing

At some point in every developer’s journey, you notice a familiar pain: the same computation is being done again… and again… and again.

Fetching the same data. Parsing the same input. Calculating the same expensive result

Memoization is the functional programming idea that turns this repetition into memory.

What Is Memoization?

Memoization is a technique where a function remembers the results of previous calls.

If the function is called again with the same input, it doesn’t recompute the result — it simply returns the stored value.

The key requirement is crucial:

Memoization works correctly only for pure functions.

That means:

• Same input → same output

• No hidden state

• No side effects

Simple Memoize function

https://storage.googleapis.com/lambdabricks-cd393.firebasestorage.app/memoization.svg?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=firebase-adminsdk-fbsvc%40lambdabricks-cd393.iam.gserviceaccount.com%2F20260117%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20260117T134208Z&X-Goog-Expires=3600&X-Goog-SignedHeaders=host&X-Goog-Signature=1c915e8ee72dfdfdd027fc78dbc90590aed2886697a49e1681de94b6b4aee8fed33c1cf87d4347324c4fcc5ed11d50e6ac82158ace0fd0e119e6f370badd29d9fbef5b206dd80faaf515056aff1f5e10c288f74fdf44041049e27534f8ac288e2834b54ed55f16f14549106313447f1f9157e1433fa15255b20953de9cb390271eda8e9109d21378617eaddf5fa83f65166440e5e0d7d728116b5b65266f75cb36161000ef1e5975e00c650b7a80db1d21e8dfca5c648088bd7e520bdae32d81db88a8b4ae83388f3ffb4a643aa9b8b677794e708deb4dd3e5180a0bd8ac64bbd778735f291f6f826dc37072ce08c5cf22860f055012cb09678f7496092cdf41
fun <A, B> memoize(f: (A) -> B): (A) -> B {
    val cache = mutableMapOf<A, B>()
    return { a ->
        cache.getOrPut(a) {
            f(a)
        }
    }
}

This higher-order function:

  • Takes a function f
  • Stores results internally
  • Returns a new function with memory

    Seeing Memoization in Action

    fun slowCalculation(x: Int): Int {
        Thread.sleep(2000)
        return x * x
    }
    
    val memoized = memoize(::slowCalculation)
    
    memoized(4) // slow
    memoized(4) // instant

    The first call does the work. The second call reuses the answer. That speed difference is memoization revealing itself.

    Where Memoization Helps — and Where It Quietly Hurts

    When I first learned memoization, I treated it like a clever trick: “Store results, save time, move on.”

    But the longer you work with real systems — especially UI, backend APIs, or Android apps — the more you realize memoization isn’t just an optimization. It’s a promise. And if that promise is broken, the bugs are subtle and painful.

    So here’s the honest version.

    Where Memoization Actually Works Well

    Memoization feels natural when the code already feels mathematical.

    Take something like this:

    val square = memoize { x: Int -> x * x }

    There’s no surprise here.

    Four squared will always be sixteen. Today, tomorrow, after a reboot — always sixteen.

    That predictability is what memoization feeds on.

    The same thing happens with expensive logic. If a function takes noticeable time to run and you keep calling it with the same input, memoization suddenly feels magical. You wait once, and after that the result just shows up. That’s not cheating — that’s remembering work you’ve already done.

    This shows up a lot in data-heavy code: parsing, transformations, validations, formatting. Things that don’t touch the outside world and don’t depend on time.

    UI code is another place where memoization quietly shines. In Compose, for example, you don’t think about memoization, but it’s there:

    val total by remember(items) {
        derivedStateOf { items.sumOf { it.price } }
    }

    You’re saying: “Only redo this when the inputs change.

    That’s memoization with a lifecycle — and that’s why it works so well.

    The same goes for batch jobs or short-lived workflows. If you create a memoized function inside a function, let it do its job, and then let it die, you get all the benefits with none of the cleanup headaches.

    Where Memoization Starts to Betray You

    Now let’s talk about where memoization looks useful, but isn’t.

    Time is the simplest example.

    val now = memoize { System.currentTimeMillis() }

    The first call works. The second call lies.

    Memoization doesn’t understand time. It only understands inputs. If the input never changes, neither will the output — and suddenly “now” means “the first time this ran”.

    Randomness is the same story. If you memoize a random number generator, you don’t get better randomness — you get a constant pretending to be random.

    Side effects are even worse. Logging, printing, writing to disk — memoization simply skips them after the first run. The program still returns values, but half of its behavior has silently disappeared.

    Another place memoization hurts is with data that changes underneath you. Network responses, user profiles, feature flags — these things evolve. If you memoize them without a clear invalidation strategy, you’re not caching anymore — you’re lying to your app.

    And sometimes memoization just isn’t worth it. If a function is cheap — like adding one to a number — the cost of checking the cache is actually higher than recomputing the value. In those cases, memoization doesn’t optimize anything. It just adds complexity.

    Finally, there’s the most dangerous version: global memoization. A cache that lives forever, owned by nobody, cleared by nothing. These are memory leaks with good intentions, especially on Android.

    The Rule I Eventually Learned

    Before memoizing anything, I now ask myself three questions:

  • Does this function behave the same every time for the same input?
  • Will I actually see the same inputs again?
  • Do I know exactly when this cache should die?
  • If I hesitate on any of those, I don’t memoize.

    A More Honest Summary

    Memoization is great when your code behaves like math: pure, deterministic, and repeatable. It works beautifully for expensive computations, derived UI state, validation logic, and scoped batch work. It breaks down when time, randomness, side effects, or changing data enter the picture. And it becomes dangerous when caches outlive the problems they were meant to solve.

    Memoization isn’t about remembering everything. It’s about remembering only what’s safe to remember — and forgetting the rest.