A memoizable thing takes itself as input and produces itself.
Usually you will use this for functions:
foo :: Memoizable (String -> String),
which desugars to
foo :: (String -> String) -> String -> String
A memoizer from a to b takes a function with input a and output b and memoizes it
If you have a
Memo Foo from
then it is also a
Memoizer Foo b, which can unify
with any type
Given a memoizable function and a memoizer, put two and two together!
Your memoizable should look something like this:
foo :: Memoizable (Foo -> Bar) foo self = go where go x = ... self a ...
The main feature is that
self is the first input
self and is used for all recursive calls.
Memoizables can take as many arguments as you like, given an appropriate Memoizer
foo2 :: Memoizable (Bar -> Baz -> Quux) foo2 self = go where go x y = ... self a b ...
Data.MemoCombinators, for example,
you could do
runMemo (Memo.memo2 Memo.bar Memo.baz) foo2