# Best practice approach for caching data in objects

**URL:** <https://discourse.julialang.org/t/best-practice-approach-for-caching-data-in-objects/20419>\
**Category:** General Usage\
**Tags:** question, type, design\
**Created:** [February 4, 2019, 9:20am UTC](https://discourse.julialang.org/t/best-practice-approach-for-caching-data-in-objects/20419 "2019-02-04T09:20:10Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![mkborregaard](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkborregaard/32/556_2.png) [@mkborregaard](https://discourse.julialang.org/u/mkborregaard)\
**Post date:** [February 4, 2019, 9:20am UTC](https://discourse.julialang.org/t/best-practice-approach-for-caching-data-in-objects/20419/1 "2019-02-04T09:20:10Z")

</div>

Something that’s come up repeatedly for me this last week in different contexts (and also many times before) is how do you approach the case where you sometimes want to be able to store intermediary results?

For a clearly contrived MWE, let’s say we have a type wrapping an Array and in certain contexts we often evaluate the extrema. Let’s say we don’t have to worry about cache invalidation (e.g. either 1) the values of the array don’t change or 2) we can only change the value using a setter function that flushes the cache). In a real setting the function (here `extrema`) might be expensive.

I see a number of design patterns. The most obvious are to never cache or always cache (not what I’m after):

```julia
# 1. just the type
struct Mytype
   v::Vector
end
lims(m::Mytype) = extrema(m.v)

# 2. type and cache
struct Mytype{T}
   v::Vector{T}
   lims::Tuple(T, T)
   Mytype(a::AbstractVector{T}) where T = new{T}(a, extrema(a))
end
lims(m::Mytype) = m.lims

```

But what if I want to cache the object? I see two possibilities. Either I cache it the first time it is called:

```julia
# 3. Cache at first request
struct Mytype{T}
   v::Vector{T}
   lims::Ref{Union{Nothing, Tuple{T,T}}}
   Mytype(v::AbstractVector{T}) where T = new{T}(v, nothing)
end
function lims(m::Mytype) #not `lims!` I think as the mutation doesn't change behaviour
    isnothing(m.lims[]) && (m.lims[] = extrema(m.v))
    m.lims[]
end

```

Or have a special type that one can invoke at will, giving full control to the user but at the cost of increased coding complexity for little gain:

```julia
# 4. Different types to signify cache

abstract type Myabstracttype end

struct Mytype <: Myabstracttype
   v::Vector
end

struct Mytype_cached{T} <: Myabstracttype
   v::Vector{T}
   lims::Tuple(T, T)
end

lims(m::Mytype) = extrema(m.v)
lims(m::Mytype_cached) = m.lims
cachelims_mytype(m::Mytype) = Mytype_cached{eltype(m.v)}(v, lims(v))

```

I can see pros and cons of all (as outlined above) and I do realize that coding style is a preference, but:

1. Which of these design patterns are preferred?
2. Is this even an ideomatic thing to do in Julia?

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [February 4, 2019, 9:25am UTC](https://discourse.julialang.org/t/best-practice-approach-for-caching-data-in-objects/20419/2 "2019-02-04T09:25:01Z")

</div>

I would go with the second approach (separate cached type), simply because calculating the result type for the cached object (for use in the `Ref`) is, in the general case (eg something more complex than `extrema`) can be a very difficult problem, if one does not want to rely on inference (`Base.return_types`).

---

<div class="post-metadata">

**Author:** ![mkborregaard](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkborregaard/32/556_2.png) [@mkborregaard](https://discourse.julialang.org/u/mkborregaard)\
**Post date:** [February 4, 2019, 9:43am UTC](https://discourse.julialang.org/t/best-practice-approach-for-caching-data-in-objects/20419/3 "2019-02-04T09:43:42Z")

</div>

Ok great. In my specific cases I always know the type of the cached object though?

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [February 4, 2019, 9:49am UTC](https://discourse.julialang.org/t/best-practice-approach-for-caching-data-in-objects/20419/4 "2019-02-04T09:49:31Z")

</div>

Then (1) I envy you 😉, and (2) if the calculation itself is expensive and you really want to hide this from the user, the approach with `Ref` could be fine.

The [`Matrix` package in R](https://cran.r-project.org/web/packages/Matrix/index.html) is an example of this kind of interface, caching various decompositions after first use.

That said, I always prefer to make precalculated values explicit somehow, and from a design perspective I would still prefer the second approach (trading off user convenience for a more transparent design).

---

<div class="post-metadata">

**Author:** ![bennedich](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bennedich/32/4894_2.png) [@bennedich](https://discourse.julialang.org/u/bennedich)\
**Post date:** [February 4, 2019, 10:55pm UTC](https://discourse.julialang.org/t/best-practice-approach-for-caching-data-in-objects/20419/5 "2019-02-04T22:55:55Z")

</div>

This type of caching is also called lazy initialization. I would go with something similar to your approach 4, but extract the lazy logic to be fully independent/reusable; something like this perhaps:

```julia
mutable struct Lazy{T}
   f::Function
   value::Union{Nothing, T}
   calculated::Bool
   Lazy{T}(f) where T = new{T}(f, nothing, false)
end

function value(z::Lazy{T}) where T
    z.calculated || (z.value = z.f(); z.calculated = true)
    z.value::T
end

```

I used a `calculated` field instead of `isnothing`, in case `T` is `Nothing` (which could make sense if the lazy function doesn’t need a return value, e.g. if modifying some global state). Note that this particular implementation is not thread safe.

Now your example can be written like this:

```julia
struct MyType{T}
   v::Vector{T}
   lims::Lazy{Tuple{T,T}}
   MyType(v::AbstractVector{T}) where T = new{T}(v, Lazy{Tuple{T,T}}(() -> extrema(v)))
end

lims(m::MyType) = value(m.lims)

```

Testing it:

```julia
julia> m = MyType([11, 77, 58, 4, 25, 78, 63, 5, 97, 40]);

julia> lims(m)
(4, 97)

julia> @code_warntype lims(m)
Body::Tuple{Int64,Int64}
1 ─ %1 = (Base.getfield)(m, :lims)::Lazy{Tuple{Int64,Int64}}
│ %2 = invoke Main.value(%1::Lazy{Tuple{Int64,Int64}})::Tuple{Int64,Int64}
└── return %2

```

---

<div class="post-metadata">

**Author:** ![mkborregaard](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkborregaard/32/556_2.png) [@mkborregaard](https://discourse.julialang.org/u/mkborregaard)\
**Post date:** [February 4, 2019, 11:14pm UTC](https://discourse.julialang.org/t/best-practice-approach-for-caching-data-in-objects/20419/6 "2019-02-04T23:14:26Z")

</div>

That’s nice, I like the concept. I can also use this for another case I had where I take a lazy subset of an object. This is actually my approach #3, though, right, not #4?

---

<div class="post-metadata">

**Author:** ![Ihar\_Kudraucau](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ihar_kudraucau/32/10185_2.png) [@Ihar\_Kudraucau](https://discourse.julialang.org/u/Ihar_Kudraucau)\
**Post date:** [September 9, 2019, 9:18am UTC](https://discourse.julialang.org/t/best-practice-approach-for-caching-data-in-objects/20419/7 "2019-09-09T09:18:40Z")

</div>

But what is best practice approach for caching data if the data should be updated?

What is about follow solution:

```julia
mutable struct Cache
    values_from_db::Vector{Int}
    Cache() = new(Vector{Int}())
end

struct MyData
    values_calculated::Vector{Int}
    _cache::Cache
    MyData() = new(Vector{Int}(), Cache())
end

function get(d::MyData, idx::Int)
    if idx > length(d.values_calculated)
        d.values_calculated = update(d)
    end
    d.values_calculated[idx]
end

function update(d::MyData)
    # update d._cache from database
    # update d.values_calculated according to new cached values
end

```

---

<div class="post-metadata">

**Author:** ![kristoffer.carlsson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kristoffer.carlsson/32/22_2.png) [@kristoffer.carlsson](https://discourse.julialang.org/u/kristoffer.carlsson)\
**Post date:** [September 9, 2019, 9:45am UTC](https://discourse.julialang.org/t/best-practice-approach-for-caching-data-in-objects/20419/8 "2019-09-09T09:45:07Z")

</div>

> [@bennedich](#):
>
> I used a `calculated` field instead of `isnothing` , in case `T` is `Nothing` (which could make sense if the lazy function doesn’t need a return value, e.g. if modifying some global state)

I think more common is to use

```julia
value::Union{Nothing, Some{T}}

```

and

```julia
z.value === nothing && z.value = Some(z.f())

```
