Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any way to memoize a value in Haskell?

I have the following function in Haskell:

memdb = -- load the contents of a database into memory as a Map

And then I have the following line:

map (\x -> memdb ! x) values

I would like memdb to generate the Map only once, instead of on every iteration of map. I could do it using something like this:

make_memdb = -- equivalent to memdb in previous example
memdb <- make_memdb
map (\x -> memdb ! x) values

But this would mean that I would have to pass memdb in to every function that uses it. Is there any way I can:

a. avoid recalculating memdb each time it is called OR

b. save the value produced in make_memdb as a constant so I can avoid passing it in to every function that uses it?

like image 987
Vlad the Impala Avatar asked Aug 26 '11 20:08

Vlad the Impala


People also ask

What is Memoized value?

In programming, memoization is an optimization technique that makes applications more efficient and hence faster. It does this by storing computation results in cache, and retrieving that same information from the cache the next time it's needed instead of computing it again.

How do you memoize a function in Python?

To memoize a function in Python, we can use a utility supplied in Python's standard library—the functools. lru_cache decorator. Now, every time you run the decorated function, lru_cache will check for a cached result for the inputs provided. If the result is in the cache, lru_cache will return it.

How do you memoize a function in C++?

The right way to do memoization in C++ is to mix the Y-combinator in. Your base function needs a modification. Instead of calling itself directly, it takes a templateized reference to itself as its first argument (or, a std::function<Same_Signature> recursion as its first argument). We start with a Y-combinator.

What is memoize angular?

In computing, memoization or memoisation is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again.


2 Answers

As your map comes from a database, that means it cannot be a constant as it can be different between runs of your application.

But this would mean that I would have to pass memdb in to every function that uses it.

Yes, but there are tools to make this less bad than it sounds. In particular, this sounds like a perfect use case for the the reader monad!

Reader monads are commonly used when you have some value, like a configuration, that you want to load at the start of your program and then be able to access it around your program without having to explicitly pass it around all the time. Here's a short example of how you would use it:

main = do
    memdb <- make_memdb -- Get the memdb from the database once and for all
    runReaderT foo memdb

foo = do
    memdb <- ask -- Grab the memdb. Will not reload from the database
    liftIO $ putStrLn "Hello, world" -- IO actions have to be lifted
    -- [...]

See also:

  • Real World Haskell: The reader monad
  • Learn You a Haskell: Reader? Ugh, not this joke again
like image 105
hammar Avatar answered Sep 30 '22 12:09

hammar


You seem to want to get the memdb via IO as a way of avoiding passing more parameters, correct? You then ask if you can either (A) define memdb, implying it will be a top-level function, without the overhead of loading data from the DB or (B) if you can save the loaded data structure with global scope.

Both of these are doable with an IORef and unsafePerformIO to define a top-level mutable global variable. I DO NOT SUGGEST YOU DO THIS. It is clumsy and annoying to refactor. That said, I'll show you how anyway:

Assuming you have a function:

make_memdb :: IO (Map K V)

You can declare a top-level mutable variable:

import Data.Map as M
import Data.IORef

mRef :: IORef (Map K V)
mRef = unsafePerformIO $ newIORef M.empty
{-# NOINLINE mRef #-}

main = do
    m <- make_memdb
    writeIORef mRef m
    ... do  stuff using mRef ...

stuffUsingMRef ... = do
    memdb <- readIORef
    let vs = map (memdb !) values
    return vs

Notice that your functions will forever live in IO. This is because you need IO in order to read the global mutable variable in which you placed memdb. If you don't like this, and you don't like passing parameters, then learn the state monad! I'm sure another answer will discuss that, which is the correct solution.

like image 38
Thomas M. DuBuisson Avatar answered Sep 30 '22 10:09

Thomas M. DuBuisson