-module(rememo@memo). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/rememo/memo.gleam"). -export([create/1, set/3, get/2, memoize/3]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. ?MODULEDOC(" The cache is a [Erlang Term Storage database](https://www.erlang.org/doc/apps/erts/persistent_term.html).\n"). -file("src/rememo/memo.gleam", 20). ?DOC( " Make a new memoization cache that will be used for the rest of the inner scope\n" " of the provided function.\n" " Pass this cache to the function you want to memoize.\n" " \n" " This is best used with a `use` expression:\n" " ```gleam\n" " use cache <- create()\n" " f(a, b, c, cache)\n" " ```\n" ). -spec create(fun((internal@carpenter@table:set(any(), any())) -> IDL)) -> IDL. create(Fun) -> Table_name = begin _pipe = tempo@instant:now(), gleam@string:inspect(_pipe) end, Cache_table@1 = case begin _pipe@1 = internal@carpenter@table:build(Table_name), _pipe@2 = internal@carpenter@table:privacy(_pipe@1, private), _pipe@3 = internal@carpenter@table:write_concurrency( _pipe@2, auto_write_concurrency ), _pipe@4 = internal@carpenter@table:read_concurrency(_pipe@3, true), _pipe@5 = internal@carpenter@table:decentralized_counters(_pipe@4, true), _pipe@6 = internal@carpenter@table:compression(_pipe@5, false), internal@carpenter@table:set(_pipe@6) end of {ok, Cache_table} -> Cache_table; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"rememo/memo"/utf8>>, function => <<"create"/utf8>>, line => 23, value => _assert_fail, start => 659, 'end' => 927, pattern_start => 670, pattern_end => 685}) end, Result = Fun(Cache_table@1), internal@carpenter@table:drop(Cache_table@1), Result. -file("src/rememo/memo.gleam", 39). ?DOC( " Manually add a key-value pair to the memoization cache.\n" " Useful if you need to pre-seed the cache with a starting value, for example.\n" ). -spec set(internal@carpenter@table:set(IDN, IDO), IDN, IDO) -> nil. set(Cache, Key, Value) -> internal@carpenter@table:insert(Cache, [{Key, Value}]). -file("src/rememo/memo.gleam", 45). ?DOC( " Manually look up a value from the memoization cache for a given key.\n" " Useful if you want to also return intermediate results as well as a final result, for example.\n" ). -spec get(internal@carpenter@table:set(IDR, IDS), IDR) -> {ok, IDS} | {error, nil}. get(Cache, Key) -> case internal@carpenter@table:lookup(Cache, Key) of [] -> {error, nil}; [{_, V} | _] -> {ok, V} end. -file("src/rememo/memo.gleam", 65). ?DOC( " Look up the value associated with the given key in the memoization cache,\n" " and return it if it exists. If it doesn't exist, evaluate the callback function\n" " update the cache with the key and the corresponding value the callback returned, \n" " and return that value.\n" " \n" " This works well with a `use` expression:\n" " ```gleam\n" " fn f(a, b, c, cache) {\n" " use <- memoize(cache, #(a, b, c))\n" " // function body goes here\n" " }\n" " ```\n" ). -spec memoize(internal@carpenter@table:set(IDX, IDY), IDX, fun(() -> IDY)) -> IDY. memoize(Cache, Key, Fun) -> case get(Cache, Key) of {ok, Value} -> Value; {error, nil} -> Result = Fun(), set(Cache, Key, Result), Result end.