1 people like it.
Like the snippet!
Thread-safe Generic MemoryCache and Memoize Function
A generic, thread-safe version of a MemoryCache based on ConcurrentDictionary, and a memoize function implemented using the MemoryCache.
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
|
open System
open System.Collections.Concurrent
open System.Collections.Generic
open System.Timers
type CacheExpirationPolicy =
| NoExpiration
| AbsoluteExpiration of TimeSpan
| SlidingExpiration of TimeSpan
type CacheEntryExpiration =
| NeverExpires
| ExpiresAt of DateTime
| ExpiresAfter of TimeSpan
type CacheEntry<'key, 'value> =
{
Key: 'key
Value: 'value
Expiration: CacheEntryExpiration
LastUsage: DateTime
}
module CacheExpiration =
let isExpired (entry: CacheEntry<_,_>) =
match entry.Expiration with
| NeverExpires -> false
| ExpiresAt date -> DateTime.UtcNow > date
| ExpiresAfter window -> (DateTime.UtcNow - entry.LastUsage) > window
type private IMemoryCacheStore<'key, 'value> =
inherit IEnumerable<CacheEntry<'key, 'value>>
abstract member Add: CacheEntry<'key, 'value> -> unit
abstract member GetOrAdd: 'key -> ('key -> CacheEntry<'key, 'value>) -> CacheEntry<'key, 'value>
abstract member Remove: 'key -> unit
abstract member Contains: 'key -> bool
abstract member Update: 'key -> (CacheEntry<'key, 'value> -> CacheEntry<'key, 'value>) -> unit
abstract member TryFind: 'key -> CacheEntry<'key, 'value> option
type MemoryCache<'key, 'value> (?cacheExpirationPolicy) =
let policy = defaultArg cacheExpirationPolicy NoExpiration
let store =
let entries = ConcurrentDictionary<'key, CacheEntry<'key, 'value>>()
let get, getEnumerator =
let values = entries |> Seq.map (fun kvp -> kvp.Value)
(fun () -> values), (fun () -> values.GetEnumerator())
{new IMemoryCacheStore<'key, 'value> with
member __.Add entry = entries.AddOrUpdate(entry.Key, entry, fun _ _ -> entry) |> ignore
member __.GetOrAdd key getValue = entries.GetOrAdd(key, getValue)
member __.Remove key = entries.TryRemove key |> ignore
member __.Contains key = entries.ContainsKey key
member __.Update key update =
match entries.TryGetValue(key) with
| (true, entry) -> entries.AddOrUpdate(key, entry, fun _ entry -> update entry) |> ignore
| _ -> ()
member __.TryFind key =
match entries.TryGetValue(key) with
| (true, entry) -> Some entry
| _ -> None
member __.GetEnumerator () = getEnumerator ()
member __.GetEnumerator () = getEnumerator () :> Collections.IEnumerator
}
let checkExpiration () =
store
|> Seq.filter CacheExpiration.isExpired
|> Seq.map (fun entry -> entry.Key)
|> Seq.iter store.Remove
let newCacheEntry key value =
{ Key = key
Value = value
Expiration = match policy with
| NoExpiration -> NeverExpires
| AbsoluteExpiration time -> ExpiresAt (DateTime.UtcNow + time)
| SlidingExpiration window -> ExpiresAfter window
LastUsage = DateTime.UtcNow
}
let add key value =
if key |> store.Contains
then store.Update key (fun entry -> {entry with Value = value; LastUsage = DateTime.UtcNow})
else store.Add <| newCacheEntry key value
let remove key =
store.Remove key
let get key =
store.TryFind key |> Option.bind (fun entry -> Some entry.Value)
let getOrAdd key value =
store.GetOrAdd key (fun _ -> newCacheEntry key value)
|> fun entry -> entry.Value
let getOrAddResult key f =
store.GetOrAdd key (fun _ -> newCacheEntry key <| f())
|> fun entry -> entry.Value
let getTimer (expiration: TimeSpan) =
if expiration.TotalSeconds < 1.0
then TimeSpan.FromMilliseconds 100.0
elif expiration.TotalMinutes < 1.0
then TimeSpan.FromSeconds 1.0
else TimeSpan.FromMinutes 1.0
|> fun interval -> new Timer(interval.TotalMilliseconds)
let timer =
match policy with
| NoExpiration -> None
| AbsoluteExpiration time -> time |> getTimer |> Some
| SlidingExpiration time -> time |> getTimer |> Some
let observer =
match timer with
| Some t ->
let disposable = t.Elapsed |> Observable.subscribe (fun _ -> checkExpiration())
t.Start()
Some disposable
| None -> None
member __.Add key value = add key value
member __.Remove key = remove key
member __.Get key = get key
member __.GetOrAdd key value = getOrAdd key value
member __.GetOrAddResult key f = getOrAddResult key f
[<AutoOpen>]
module Memoization =
type private MemoizationCache<'a,'b when 'a: equality> (?cacheExpirationPolicy) =
let cache =
match cacheExpirationPolicy with
| Some policy -> new MemoryCache<string,'b>(policy)
| None -> new MemoryCache<string,'b>()
let getKey (key: 'a) =
if key |> box |> isNull
then typeof<'a>.FullName
else sprintf "%s_%d" typeof<'a>.FullName <| key.GetHashCode()
member __.TryGetValue key =
let keyString = getKey key
cache.Get keyString
member __.GetOrAdd key getValue =
let keyString = getKey key
cache.GetOrAddResult keyString getValue
let private memoizeWithCache f (cache: MemoizationCache<_,_>) x =
(fun () -> f x) |> cache.GetOrAdd x
/// Create a new function that remembers the results of the given function
/// for each unique input parameter, returning the cached result instead
/// of recomputing it each time the function is called, optimizing the execution speed
/// at the cost of increased memory usage.
///
/// Note: This optimization should only be used with referentially transparent functions
let memoize f =
new MemoizationCache<_,_>() |> memoizeWithCache f
/// Create a new function that remembers the results of the given function
/// for each unique input parameter, returning the cached result instead
/// of recomputing it each time the function is called, optimizing the execution speed
/// at the cost of increased memory usage. Uses a specified Cache Expiration Policy
/// to limit the amount of time the results are stored, to allow memory to be free'd
/// after a certain amount of time.
///
/// Note: This optimization should only be used with referentially transparent functions
let memoizeWithExpiration policy f =
new MemoizationCache<_,_>(policy) |> memoizeWithCache f
|
namespace System
namespace System.Collections
namespace System.Collections.Concurrent
namespace System.Collections.Generic
namespace System.Timers
type CacheExpirationPolicy =
| NoExpiration
| AbsoluteExpiration of TimeSpan
| SlidingExpiration of TimeSpan
Full name: Script.CacheExpirationPolicy
union case CacheExpirationPolicy.NoExpiration: CacheExpirationPolicy
union case CacheExpirationPolicy.AbsoluteExpiration: TimeSpan -> CacheExpirationPolicy
Multiple items
type TimeSpan =
struct
new : ticks:int64 -> TimeSpan + 3 overloads
member Add : ts:TimeSpan -> TimeSpan
member CompareTo : value:obj -> int + 1 overload
member Days : int
member Duration : unit -> TimeSpan
member Equals : value:obj -> bool + 1 overload
member GetHashCode : unit -> int
member Hours : int
member Milliseconds : int
member Minutes : int
...
end
Full name: System.TimeSpan
--------------------
TimeSpan()
TimeSpan(ticks: int64) : unit
TimeSpan(hours: int, minutes: int, seconds: int) : unit
TimeSpan(days: int, hours: int, minutes: int, seconds: int) : unit
TimeSpan(days: int, hours: int, minutes: int, seconds: int, milliseconds: int) : unit
union case CacheExpirationPolicy.SlidingExpiration: TimeSpan -> CacheExpirationPolicy
type CacheEntryExpiration =
| NeverExpires
| ExpiresAt of DateTime
| ExpiresAfter of TimeSpan
Full name: Script.CacheEntryExpiration
union case CacheEntryExpiration.NeverExpires: CacheEntryExpiration
union case CacheEntryExpiration.ExpiresAt: DateTime -> CacheEntryExpiration
Multiple items
type DateTime =
struct
new : ticks:int64 -> DateTime + 10 overloads
member Add : value:TimeSpan -> DateTime
member AddDays : value:float -> DateTime
member AddHours : value:float -> DateTime
member AddMilliseconds : value:float -> DateTime
member AddMinutes : value:float -> DateTime
member AddMonths : months:int -> DateTime
member AddSeconds : value:float -> DateTime
member AddTicks : value:int64 -> DateTime
member AddYears : value:int -> DateTime
...
end
Full name: System.DateTime
--------------------
DateTime()
(+0 other overloads)
DateTime(ticks: int64) : unit
(+0 other overloads)
DateTime(ticks: int64, kind: DateTimeKind) : unit
(+0 other overloads)
DateTime(year: int, month: int, day: int) : unit
(+0 other overloads)
DateTime(year: int, month: int, day: int, calendar: Globalization.Calendar) : unit
(+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int) : unit
(+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, kind: DateTimeKind) : unit
(+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, calendar: Globalization.Calendar) : unit
(+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int) : unit
(+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int, kind: DateTimeKind) : unit
(+0 other overloads)
union case CacheEntryExpiration.ExpiresAfter: TimeSpan -> CacheEntryExpiration
type CacheEntry<'key,'value> =
{Key: 'key;
Value: 'value;
Expiration: CacheEntryExpiration;
LastUsage: DateTime;}
Full name: Script.CacheEntry<_,_>
CacheEntry.Key: 'key
CacheEntry.Value: 'value
CacheEntry.Expiration: CacheEntryExpiration
CacheEntry.LastUsage: DateTime
val isExpired : entry:CacheEntry<'a,'b> -> bool
Full name: Script.CacheExpiration.isExpired
val entry : CacheEntry<'a,'b>
val date : DateTime
property DateTime.UtcNow: DateTime
val window : TimeSpan
type private IMemoryCacheStore<'key,'value> =
interface
inherit IEnumerable<CacheEntry<'key,'value>>
abstract member Add : CacheEntry<'key,'value> -> unit
abstract member Contains : 'key -> bool
abstract member GetOrAdd : 'key -> ('key -> CacheEntry<'key,'value>) -> CacheEntry<'key,'value>
abstract member Remove : 'key -> unit
abstract member TryFind : 'key -> CacheEntry<'key,'value> option
abstract member Update : 'key -> (CacheEntry<'key,'value> -> CacheEntry<'key,'value>) -> unit
end
Full name: Script.IMemoryCacheStore<_,_>
type IEnumerable<'T> =
member GetEnumerator : unit -> IEnumerator<'T>
Full name: System.Collections.Generic.IEnumerable<_>
abstract member private IMemoryCacheStore.Add : CacheEntry<'key,'value> -> unit
Full name: Script.IMemoryCacheStore`2.Add
type unit = Unit
Full name: Microsoft.FSharp.Core.unit
abstract member private IMemoryCacheStore.GetOrAdd : 'key -> ('key -> CacheEntry<'key,'value>) -> CacheEntry<'key,'value>
Full name: Script.IMemoryCacheStore`2.GetOrAdd
abstract member private IMemoryCacheStore.Remove : 'key -> unit
Full name: Script.IMemoryCacheStore`2.Remove
abstract member private IMemoryCacheStore.Contains : 'key -> bool
Full name: Script.IMemoryCacheStore`2.Contains
type bool = Boolean
Full name: Microsoft.FSharp.Core.bool
abstract member private IMemoryCacheStore.Update : 'key -> (CacheEntry<'key,'value> -> CacheEntry<'key,'value>) -> unit
Full name: Script.IMemoryCacheStore`2.Update
abstract member private IMemoryCacheStore.TryFind : 'key -> CacheEntry<'key,'value> option
Full name: Script.IMemoryCacheStore`2.TryFind
type 'T option = Option<'T>
Full name: Microsoft.FSharp.Core.option<_>
Multiple items
type MemoryCache<'key,'value> =
new : ?cacheExpirationPolicy:CacheExpirationPolicy -> MemoryCache<'key,'value>
member Add : key:'key -> value:'value -> unit
member Get : key:'key -> 'value option
member GetOrAdd : key:'key -> value:'value -> 'value
member GetOrAddResult : key:'key -> f:(unit -> 'value) -> 'value
member Remove : key:'key -> unit
Full name: Script.MemoryCache<_,_>
--------------------
new : ?cacheExpirationPolicy:CacheExpirationPolicy -> MemoryCache<'key,'value>
val cacheExpirationPolicy : CacheExpirationPolicy option
val policy : CacheExpirationPolicy
val defaultArg : arg:'T option -> defaultValue:'T -> 'T
Full name: Microsoft.FSharp.Core.Operators.defaultArg
val store : IMemoryCacheStore<'key,'value>
val entries : ConcurrentDictionary<'key,CacheEntry<'key,'value>>
Multiple items
type ConcurrentDictionary<'TKey,'TValue> =
new : unit -> ConcurrentDictionary<'TKey, 'TValue> + 6 overloads
member AddOrUpdate : key:'TKey * addValueFactory:Func<'TKey, 'TValue> * updateValueFactory:Func<'TKey, 'TValue, 'TValue> -> 'TValue + 1 overload
member Clear : unit -> unit
member ContainsKey : key:'TKey -> bool
member Count : int
member GetEnumerator : unit -> IEnumerator<KeyValuePair<'TKey, 'TValue>>
member GetOrAdd : key:'TKey * valueFactory:Func<'TKey, 'TValue> -> 'TValue + 1 overload
member IsEmpty : bool
member Item : 'TKey -> 'TValue with get, set
member Keys : ICollection<'TKey>
...
Full name: System.Collections.Concurrent.ConcurrentDictionary<_,_>
--------------------
ConcurrentDictionary() : unit
ConcurrentDictionary(collection: IEnumerable<KeyValuePair<'TKey,'TValue>>) : unit
ConcurrentDictionary(comparer: IEqualityComparer<'TKey>) : unit
ConcurrentDictionary(concurrencyLevel: int, capacity: int) : unit
ConcurrentDictionary(collection: IEnumerable<KeyValuePair<'TKey,'TValue>>, comparer: IEqualityComparer<'TKey>) : unit
ConcurrentDictionary(concurrencyLevel: int, collection: IEnumerable<KeyValuePair<'TKey,'TValue>>, comparer: IEqualityComparer<'TKey>) : unit
ConcurrentDictionary(concurrencyLevel: int, capacity: int, comparer: IEqualityComparer<'TKey>) : unit
val get : (unit -> seq<CacheEntry<'key,'value>>)
val getEnumerator : (unit -> IEnumerator<CacheEntry<'key,'value>>)
val values : seq<CacheEntry<'key,'value>>
module Seq
from Microsoft.FSharp.Collections
val map : mapping:('T -> 'U) -> source:seq<'T> -> seq<'U>
Full name: Microsoft.FSharp.Collections.Seq.map
val kvp : KeyValuePair<'key,CacheEntry<'key,'value>>
property KeyValuePair.Value: CacheEntry<'key,'value>
IEnumerable.GetEnumerator() : IEnumerator<CacheEntry<'key,'value>>
val entry : CacheEntry<'key,'value>
ConcurrentDictionary.AddOrUpdate(key: 'key, addValue: CacheEntry<'key,'value>, updateValueFactory: Func<'key,CacheEntry<'key,'value>,CacheEntry<'key,'value>>) : CacheEntry<'key,'value>
ConcurrentDictionary.AddOrUpdate(key: 'key, addValueFactory: Func<'key,CacheEntry<'key,'value>>, updateValueFactory: Func<'key,CacheEntry<'key,'value>,CacheEntry<'key,'value>>) : CacheEntry<'key,'value>
val ignore : value:'T -> unit
Full name: Microsoft.FSharp.Core.Operators.ignore
val __ : IMemoryCacheStore<'key,'value>
abstract member private IMemoryCacheStore.GetOrAdd : 'key -> ('key -> CacheEntry<'key,'value>) -> CacheEntry<'key,'value>
val key : 'key
val getValue : ('key -> CacheEntry<'key,'value>)
ConcurrentDictionary.GetOrAdd(key: 'key, value: CacheEntry<'key,'value>) : CacheEntry<'key,'value>
ConcurrentDictionary.GetOrAdd(key: 'key, valueFactory: Func<'key,CacheEntry<'key,'value>>) : CacheEntry<'key,'value>
abstract member private IMemoryCacheStore.Remove : 'key -> unit
ConcurrentDictionary.TryRemove(key: 'key, value: byref<CacheEntry<'key,'value>>) : bool
abstract member private IMemoryCacheStore.Contains : 'key -> bool
ConcurrentDictionary.ContainsKey(key: 'key) : bool
abstract member private IMemoryCacheStore.Update : 'key -> (CacheEntry<'key,'value> -> CacheEntry<'key,'value>) -> unit
val update : (CacheEntry<'key,'value> -> CacheEntry<'key,'value>)
ConcurrentDictionary.TryGetValue(key: 'key, value: byref<CacheEntry<'key,'value>>) : bool
abstract member private IMemoryCacheStore.TryFind : 'key -> CacheEntry<'key,'value> option
union case Option.Some: Value: 'T -> Option<'T>
union case Option.None: Option<'T>
Multiple items
namespace System.Collections
--------------------
namespace Microsoft.FSharp.Collections
type IEnumerator =
member Current : obj
member MoveNext : unit -> bool
member Reset : unit -> unit
Full name: System.Collections.IEnumerator
val checkExpiration : (unit -> unit)
val filter : predicate:('T -> bool) -> source:seq<'T> -> seq<'T>
Full name: Microsoft.FSharp.Collections.Seq.filter
module CacheExpiration
from Script
val iter : action:('T -> unit) -> source:seq<'T> -> unit
Full name: Microsoft.FSharp.Collections.Seq.iter
val newCacheEntry : ('a -> 'b -> CacheEntry<'a,'b>)
val key : 'a
val value : 'b
val time : TimeSpan
val add : ('key -> 'value -> unit)
val value : 'value
abstract member private IMemoryCacheStore.Add : CacheEntry<'key,'value> -> unit
val remove : ('key -> unit)
val get : ('key -> 'value option)
module Option
from Microsoft.FSharp.Core
val bind : binder:('T -> 'U option) -> option:'T option -> 'U option
Full name: Microsoft.FSharp.Core.Option.bind
val getOrAdd : ('key -> 'value -> 'value)
val getOrAddResult : ('key -> (unit -> 'value) -> 'value)
val f : (unit -> 'value)
val getTimer : (TimeSpan -> Timer)
val expiration : TimeSpan
property TimeSpan.TotalSeconds: float
TimeSpan.FromMilliseconds(value: float) : TimeSpan
property TimeSpan.TotalMinutes: float
TimeSpan.FromSeconds(value: float) : TimeSpan
TimeSpan.FromMinutes(value: float) : TimeSpan
val interval : TimeSpan
Multiple items
type Timer =
inherit Component
new : unit -> Timer + 1 overload
member AutoReset : bool with get, set
member BeginInit : unit -> unit
member Close : unit -> unit
member Enabled : bool with get, set
member EndInit : unit -> unit
member Interval : float with get, set
member Site : ISite with get, set
member Start : unit -> unit
member Stop : unit -> unit
...
Full name: System.Timers.Timer
--------------------
Timer() : unit
Timer(interval: float) : unit
property TimeSpan.TotalMilliseconds: float
val timer : Timer option
val observer : IDisposable option
val t : Timer
val disposable : IDisposable
event Timer.Elapsed: IEvent<ElapsedEventHandler,ElapsedEventArgs>
module Observable
from Microsoft.FSharp.Control
val subscribe : callback:('T -> unit) -> source:IObservable<'T> -> IDisposable
Full name: Microsoft.FSharp.Control.Observable.subscribe
Timer.Start() : unit
member MemoryCache.Add : key:'key -> value:'value -> unit
Full name: Script.MemoryCache`2.Add
val __ : MemoryCache<'key,'value>
member MemoryCache.Remove : key:'key -> unit
Full name: Script.MemoryCache`2.Remove
member MemoryCache.Get : key:'key -> 'value option
Full name: Script.MemoryCache`2.Get
member MemoryCache.GetOrAdd : key:'key -> value:'value -> 'value
Full name: Script.MemoryCache`2.GetOrAdd
member MemoryCache.GetOrAddResult : key:'key -> f:(unit -> 'value) -> 'value
Full name: Script.MemoryCache`2.GetOrAddResult
Multiple items
type AutoOpenAttribute =
inherit Attribute
new : unit -> AutoOpenAttribute
new : path:string -> AutoOpenAttribute
member Path : string
Full name: Microsoft.FSharp.Core.AutoOpenAttribute
--------------------
new : unit -> AutoOpenAttribute
new : path:string -> AutoOpenAttribute
module Memoization
from Script
Multiple items
type private MemoizationCache<'a,'b (requires equality)> =
new : ?cacheExpirationPolicy:CacheExpirationPolicy -> MemoizationCache<'a,'b>
member GetOrAdd : key:'a -> getValue:(unit -> 'b) -> 'b
member TryGetValue : key:'a -> 'b option
Full name: Script.Memoization.MemoizationCache<_,_>
--------------------
private new : ?cacheExpirationPolicy:CacheExpirationPolicy -> MemoizationCache<'a,'b>
val cache : MemoryCache<string,'b>
Multiple items
val string : value:'T -> string
Full name: Microsoft.FSharp.Core.Operators.string
--------------------
type string = String
Full name: Microsoft.FSharp.Core.string
val getKey : ('a -> string) (requires equality)
val key : 'a (requires equality)
val box : value:'T -> obj
Full name: Microsoft.FSharp.Core.Operators.box
val isNull : value:'T -> bool (requires 'T : null)
Full name: Microsoft.FSharp.Core.Operators.isNull
val typeof<'T> : Type
Full name: Microsoft.FSharp.Core.Operators.typeof
val sprintf : format:Printf.StringFormat<'T> -> 'T
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.sprintf
Object.GetHashCode() : int
member private MemoizationCache.TryGetValue : key:'a -> 'b option
Full name: Script.Memoization.MemoizationCache`2.TryGetValue
val keyString : string
member MemoryCache.Get : key:'key -> 'value option
val __ : MemoizationCache<'a,'b> (requires equality)
member private MemoizationCache.GetOrAdd : key:'a -> getValue:(unit -> 'b) -> 'b
Full name: Script.Memoization.MemoizationCache`2.GetOrAdd
val getValue : (unit -> 'b)
member MemoryCache.GetOrAddResult : key:'key -> f:(unit -> 'value) -> 'value
val private memoizeWithCache : f:('a -> 'b) -> cache:MemoizationCache<'a,'b> -> x:'a -> 'b (requires equality)
Full name: Script.Memoization.memoizeWithCache
val f : ('a -> 'b) (requires equality)
val cache : MemoizationCache<'a,'b> (requires equality)
val x : 'a (requires equality)
member private MemoizationCache.GetOrAdd : key:'a -> getValue:(unit -> 'b) -> 'b
val memoize : f:('a -> 'b) -> ('a -> 'b) (requires equality)
Full name: Script.Memoization.memoize
Create a new function that remembers the results of the given function
for each unique input parameter, returning the cached result instead
of recomputing it each time the function is called, optimizing the execution speed
at the cost of increased memory usage.
Note: This optimization should only be used with referentially transparent functions
val memoizeWithExpiration : policy:CacheExpirationPolicy -> f:('a -> 'b) -> ('a -> 'b) (requires equality)
Full name: Script.Memoization.memoizeWithExpiration
Create a new function that remembers the results of the given function
for each unique input parameter, returning the cached result instead
of recomputing it each time the function is called, optimizing the execution speed
at the cost of increased memory usage. Uses a specified Cache Expiration Policy
to limit the amount of time the results are stored, to allow memory to be free'd
after a certain amount of time.
Note: This optimization should only be used with referentially transparent functions
More information