How to cache function with different arguments in Nim?

Viewed 139

I'm using slow data loading and calculations and would like to cache the results, so next time it would be faster.

Example - we would like to have two functions 1) convert currency to USD and 2) calculate CPI-index (Consumer Price Index).

echo to_usd("EUR", (2000, 1, 1), false) # => 0.846
echo to_cpi("USD", (2000, 1, 1))        # => 144.0

I wonder if it would be possible to auto-generate the caching code, maybe use some library? Something like:

proc to_usd_slow*(currency: string, time: TimeD): float =
  1.0 # some slow calculations

proc to_usd* = cache(to_usd_slow)

I'm currently write cache manually, as in the code below, playground:

import hashes, tables

proc autohash*[T: tuple|object](o: T): Hash =
  for f in o.fields: result = result !& f.hash

type
  TimeD = tuple[year, month, day: int]
  PointM = tuple[time: TimeD, value: float]

# CPI - Consumer Price Index
proc to_cpi_slow(currency: string, time: TimeD, extrapolate: bool): float =
  1.0 # some slow calculations

# Cached version ---------------------------------------------------------------
type ToCpiKey = (string, TimeD, bool)
proc hash(v: ToCpiKey): Hash = v.autohash
var to_cpi_cache = init_table[ToCpiKey, float]()

proc to_cpi*(currency: string, time: TimeD, extrapolate: bool): float =
  let key: ToCpiKey = (currency, time, extrapolate)
  if key notin to_cpi_cache:
    to_cpi_cache[key] = to_cpi_slow(currency, time, extrapolate)
  to_cpi_cache[key]

# Curency conversions ----------------------------------------------------------
proc to_usd_slow*(currency: string, time: TimeD): float =
  1.0 # some slow calculations

type ToUsdMKey = (string, TimeD)
proc hash(v: ToUsdMKey): Hash = v.autohash

var to_usd_cache = init_table[ToUsdMKey, float]()
proc to_usd*(currency: string, time: TimeD): float =
  let key: ToUsdMKey = (currency, time)
  if not(key in to_usd_cache): to_usd_cache[key] = to_usd_slow(currency, time)
  to_usd_cache[key]

# Usage ------------------------------------------------------------------------
echo to_cpi("USD", (2000, 1, 1), false)
echo to_usd("EUR", (2000, 1, 1))
1 Answers

It is possible to generate caching code using macros, most likely you want to look for macro pragmas for writing necessary boilerplate for your functions.

If you want to use the existing library you might want to look at memo library.

Related