Fuzion Logo
fuzion-lang.dev — The Fuzion Language Portal
JavaScript seems to be disabled. Functionality is limited.

cache.fz


# This file is part of the Fuzion language implementation.
#
# The Fuzion language implementation is free software: you can redistribute it
# and/or modify it under the terms of the GNU General Public License as published
# by the Free Software Foundation, version 3 of the License.
#
# The Fuzion language implementation is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License along with The
# Fuzion language implementation.  If not, see <https://www.gnu.org/licenses/>.


# -----------------------------------------------------------------------
#
#  Tokiwa Software GmbH, Germany
#
#  Source code of Fuzion standard library feature cache
#
#  Author: Michael Lill (michael.lill@tokiwa.software)
#
# -----------------------------------------------------------------------

# cache result of f for cache key T
#
# This is best explained by the following example:
#
#     ex_cache is
#       # here we define the cache keys by which the results of the function will
#       # be cached in the global cache
#       cache_key(val sorted_array i32) is
#       #             ^-------- the actual type we want to cache
#       # ^------- a feature declaring a type which we use as a cache key
#       #            wrapping the actual data we want to cache
#       cache_kex(val i32) is
#       # similarly, here, i32 => the actual type we want to cache and cache_kex
#       # is the wrapping cache key
#
#       # suppose now, that we have two functions fn and fm, which take no arguments but
#       # which need a very long time for their calculations.
#
#       fn Function cache_key := (() ->
#         time.nano.sleep (time.durations.seconds 2)
#         cache_key (sorted_array_of [6,4,1,9]))
#
#       fm Function cache_kex := (() ->
#         time.nano.sleep (time.durations.seconds 3)
#         cache_kex 42)
#
#       # we define two more wrappers, which will actually call the functions if
#       # their value is not in the cache, or return the stored value if already
#       # computed
#       from_cache => (cache cache_key fn).val
#       #                              ^--- will only be computed the first time we run from_cache
#       from_cachf => (cache cache_kex fm).val
#
#       say from_cache # will take two seconds
#       say from_cache # will take virtually no time
#       say from_cachf # will take three seconds
#       say from_cache # will take virtually no time
#       say from_cachf # will take virtually no time
#       say from_cachf # will take virtually no time
#
# The cache implemented works on a global level.
#
public cache(T type, f () -> T) T =>

  # use wrapper type because we do not want to prevent
  # the use of `state` for type T
  cache_item(val T) is

  if !(effect.is_installed (state unit cache_item))
    _ := state unit cache_item unit (cache_item f()) effect_mode.default

  state_get cache_item
    .val