Reputation: 349
First, the counter function:
In C++ I can do the following to cache a value across function calls (shamelessly copied from TutorialsPoint.com):
void func( void ) {
static int i = 5; // local static variable
i++;
std::cout << "i is " << i ;
std::cout << " and count is " << count << std::endl;
}
Second, a database cacheing example:
defmodule ElixirTesting do
def test_static_function_variable do
IO.puts(get_value_from_db_cache("A"))
IO.puts(get_value_from_db_cache("B"))
IO.puts(get_value_from_db_cache("A"))
true
end
defp get_value_from_db_cache(key) do
# We want this to be static.
data_cache = %{}
if !Map.has_key?(data_cache, key) do
# Scope of this assignment is limited to local if block so does not re-bind static variable.
data_cache = Map.put_new(data_cache, key, get_value_directly_from_db(key))
end
data_cache[key]
end
@db_data %{ "A" => 3, "B" => 4, "C" => 5, "D" => 6 }
defp get_value_directly_from_db(key) do
IO.puts("Inside get_value_directly_from_db for key #{key}")
@db_data[key]
end
end
Console output:
Inside get_value_directly_from_db for key A
Inside get_value_directly_from_db for key B
Inside get_value_directly_from_db for key A
For both cases how can I achieve the same in Elixir? Or, how should I go about refactoring to accomplish the result using proper functional design?
Upvotes: 0
Views: 604
Reputation: 23184
Short answer is - you can't. Functional means you program with pure functions, so they always return the same result for the same inputs, caching something inside a function breaks that
Longer answer is - when you want to manage some state, the default in Elixir is to wrap that state in a process. A simple type of process that just keeps a piece of data is called an Agent. For example, you can start a named agent, and reference it in your function like so:
defmodule Test do
def start_link(initial_value) do
Agent.start_link(fn -> initial_value end, name: MyAgent)
end
def put(value), do: Agent.update(MyAgent, fn _ -> value end)
def get, do: Agent.get(MyAgent, fn value -> value end)
end
Test.start_link(:value1)
Test.get() |> IO.inspect() # => :value1
Test.put(:value2)
Test.get() |> IO.inspect() # => :value2
Upvotes: 2