Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you make a generic memoize function in Haskell?

Tags:

I've seen the other post about this, but is there a clean way of doing this in Haskell?

As a 2nd part, can it also be done without making the function monadic?

like image 350
Jonathan Tran Avatar asked Sep 26 '08 20:09

Jonathan Tran


People also ask

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).

What does Memoize mean?

(transitive, computing) To store (the result of a computation) so that it can be subsequently retrieved without repeating the computation.

Does Haskell Memoize functions?

The trick is to turn a function into a value because, in Haskell, functions are not memoized but values are.


2 Answers

The package data-memocombinators on hackage provides lots of reusable memoization routines. The basic idea is:

type Memo a = forall r. (a -> r) -> (a -> r) 

I.e. it can memoize any function from a. The module then provides some primitives (like unit :: Memo () and integral :: Memo Int), and combinators for building more complex memo tables (like pair :: Memo a -> Memo b -> Memo (a,b) and list :: Memo a -> Memo [a]).

like image 198
luqui Avatar answered Sep 25 '22 04:09

luqui


You can modify Jonathan´s solution with unsafePerformIO to create a "pure" memoizing version of your function.

import qualified Data.Map as Map import Data.IORef import System.IO.Unsafe  memoize :: Ord a => (a -> b) -> (a -> b) memoize f = unsafePerformIO $ do      r <- newIORef Map.empty     return $ \ x -> unsafePerformIO $ do          m <- readIORef r         case Map.lookup x m of             Just y  -> return y             Nothing -> do                      let y = f x                     writeIORef r (Map.insert x y m)                     return y 

This will work with recursive functions:

fib :: Int -> Integer fib 0 = 1 fib 1 = 1 fib n = fib_memo (n-1) + fib_memo (n-2)  fib_memo :: Int -> Integer fib_memo = memoize fib 

Altough this example is a function with one integer parameter, the type of memoize tells us that it can be used with any function that takes a comparable type. If you have a function with more than one parameter just group them in a tuple before applying memoize. F.i.:

f :: String -> [Int] -> Float f ...  f_memo = curry (memoize (uncurry f)) 
like image 39
martin lütke Avatar answered Sep 23 '22 04:09

martin lütke