TheFirstAvenger/ets

Support `fetch/2`, `fetch!/2` for sets

christhekeele opened this issue · 2 comments

It'd be nice to have a variant of ETS.Set.get/2,3 for when you want to know if a value was not found in ETS, without raising (a la ETS.Set.get!/2).

This is important if you are using ETS as a read-through cache: when looking up a value, you need to know if it was present in the cache before performing a potentially expensive operation. If that expensive operation returns nil today, there is no API provided to differentiate between the two cases.


A common access pattern in Elixir (see Map, Keyword) that enables this is to implement a fetch/2 that returns an ok tuple or error atom:

def fetch(source, key) do
  case lookup(source, key) do
    <found_value> -> {:ok, value}
    <not_found> -> :error
  end
end

Then you can implement get/2,3 and fetch!/2 on top of them:

def get(source, key, default \\ nil) do
  case fetch(source, key) do
    {:ok, value} -> {:ok, value}
    :error -> default
  end
end

def fetch!(source, key) do
  case fetch(source, key) do
    {:ok, value} -> {:ok, value}
    :error -> raise
  end
end

I propose we do this for ETS.Set (keeping get!/2 for backwards compatibility) for a more idiomatic Map-like API. The actual error tuple handling would have to be a little different to align with the consistent two-tuple errors, but the principle is the same.

I'm happy to add a PR for this, just wanted to solicit your thoughts as I get into it.

Digging into the current API further, ETS.Set.get doesn't unwrap values the way Map.get does, so implementing it as proposed would be a breaking change. Having an ETS.Set.fetch would still be useful for read-through cache reasons as mentioned above, though.

In this light, the Map.fetch! analog also doesn't make sense since since ETS.Set.get! fully implements its semantics.