Memoize.jl
Memoize.jl copied to clipboard
Running @memoize a second time silently does nothing, even if the second call refers to nonexistent names
julia> @memoize LRU(maxsize=10) f(x) = x + 1
f (generic function with 1 method)
julia> @memoize NonexistentCacheType f(x) = x + 1
f (generic function with 1 method)
julia> f(3)
4
I would expect an error (or a loud warning message) on that second call. At the very least, I'd want a loud warning in the documentation.
Currently, Memoize uses only one cache per function. It stores this cache in a global variable in the module that expands the macro. If the global variable is already defined, then Memoize doesn't construct another cache, and doesn't call the function you supply. Sadly, this means that only the first cache you give for the function is used. However, I'm not sure this will remain the case in the future. Consider the approach in #59, where each method gets a separate cache.
julia> @memoize NonexistentCacheType f(x) = x + 1
ERROR: LoadError: UndefVarError: NonexistentCacheType not defined
Stacktrace:
[1] top-level scope at none:1
[2] eval(::Module, ::Any) at ./boot.jl:331
[3] @memoize(::LineNumberNode, ::Module, ::Vararg{Any,N} where N) at /home/rik/.julia/packages/Memoize/12ANR/src/Memoize.jl:56
in expression starting at REPL[8]:1
Pretty big warning I'd say. Parts of it are also in red in the REPL.
I'd be curious to hear what people think of per-method caches. To me it makes more sense, and it supports my org's use case. But it is a breaking change, @peterahrens thank you for pointing that out about #59.
@rikhuijzer It's true that if you define the NonexistentCacheType memo function first, you get the error. However, if the function already has a cache (if you use the LRU cache first, for instance), I can reproduce that no warning is given because the macro ignores the cache type entirely.
@cstjean I'd also like to hear the opinion of other community members on this. Speaking for myself, I think that per-method caching is the most correct semantic, especially because Julia semantics already allow us to directly call different methods, and Memoize currently returns only the most specific result. For instance, consider
julia> f(x) = 1
julia> f(x::Bool) = 2
julia> @memoize g(x) = 1
julia> @memoize g(x::Bool) = 2
julia> f(true)
2
julia> invoke(f, Tuple{Integer}, true)
1
julia> g(true)
2
julia> invoke(g, Tuple{Integer}, true)
2
@rikhuijzer It's true that if you define the NonexistentCacheType memo function first, you get the error. However, if the function already has a cache (if you use the LRU cache first, for instance), I can reproduce that no warning is given because the macro ignores the cache type entirely.
Aha. Check. Got it.
julia> using Memoize, LRUCache
julia> @memoize LRU(maxsize=10) f(x) = x
f (generic function with 1 method)
julia> f(3)
3
julia> f(3)
3
julia> @memoize NonexistentCacheType f(x) = x + 1
f (generic function with 1 method)
julia> f(3)
4
julia> f(3)
4