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?
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?
If your arguments are going to be natural numbers, you can do simply:
However, that doesn't really help you with the stack overflowing, and it doesn't work with recursive calls. You can see some fancier solutions at http://www.haskell.org/haskellwiki/Memoization.
The package data-memocombinators on hackage provides lots of reusable memoization routines. The basic idea is:
I.e. it can memoize any function from a. The module then provides some primitives (like
unit :: Memo ()
andintegral :: Memo Int
), and combinators for building more complex memo tables (likepair :: Memo a -> Memo b -> Memo (a,b)
andlist :: Memo a -> Memo [a]
).You can modify Jonathan´s solution with unsafePerformIO to create a "pure" memoizing version of your function.
This will work with recursive functions:
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.:
Doing a direct translation from the more imperative languages, I came up with this.
But this is somehow unsatisfactory. Also, Data.Map constrains the parameter to be an instance of Ord.
This largely follows http://www.haskell.org/haskellwiki/Memoization.
You want a function of type (a -> b). If it doesn't call itself, then you can just write a simple wrapper that caches the return values. The best way to store this mapping depends on what properties of a you can exploit. Ordering is pretty much a minimum. With integers you can construct an infinite lazy list or tree holding the values.
or
So, suppose it is recursive. Then you need it to call not itself, but the memoized version, so you pass that in instead:
The memoized version is, of course, what we're trying to define.
But we can start by creating a function that caches its inputs:
We could construct one level by passing in a function that creates a structure that caches values. Except we need to create the version of f that already has the cached function passed in.
Thanks to laziness, this is no problem:
then all we need is to use it:
The article gives hints as to how to use a type class selecting on the input to the function to do the above, rather than choosing an explicit caching function. This can be really nice -- rather than explicitly constructing a cache for each combination of input types, we can implicitly combine caches for types a and b into a cache for a function taking a and b.
One final caveat: using this lazy technique means the cache never shrinks, it only grows. If you instead use the IO monad, you can manage this, but doing it wisely depends on usage patterns.