I'm trying to memoize a constrained generic function using what I believe is a standard memoization pattern in F#. Here is a simplified sample which summarises my failed attempt:
module Sample
open System.Collections.Generic;
let memoize f =
let cache = Dictionary<_, _>()
fun x ->
if cache.ContainsKey(x) then
cache.[x]
else
let res = f x
cache.[x] <- res
res
let memoizedFunc: ('a -> string) when 'a: equality = memoize <| (fun x ->
string x
)
The compiler complains about value restriction:
Value restriction. The value 'memoizedFunc' has been inferred to have generic type val memoizedFunc : ('_a -> string) when '_a : equality Either make the arguments to 'memoizedFunc' explicit or, if you do not intend for it to be generic, add a type annotation
As shown in the sample code above, I have already added exactly the suggested type annotation on memoizedFunc, but to no avail. The other suggestion, i.e. adding arguments (eta-conversion), will not achieve what I want with regards to memoization (each call to the function would result in a new function being cached).
My first question: how do I resolve this issue with value restriction? I could revert to a more explicit caching mechanism, but I'd prefer not to.
My second question relates to an alternative definition of the memoized function, where type parameters are added to the let binding explicitly as follows:
let memoizedFunc<'a when 'a: equality> : ('a -> string) when 'a: equality = memoize <| (fun x ->
...
I.e. the full example then becomes:
module Sample
open System.Collections.Generic;
let memoize f =
let cache = Dictionary<_, _>()
fun x ->
if cache.ContainsKey(x) then
cache.[x]
else
let res = f x
cache.[x] <- res
res
let memoizedFunc<'a when 'a: equality> : ('a -> string) when 'a: equality = memoize <| (fun x ->
string x
)
This now compiles, but does not achieve what I want: a new function is created and memoized every time I refer to memoizedFunc.
My second question: what is the significance of adding type parameters in let bindings as shown, versus omitting them? I would not have expected this to affect semantics, but rather just to aid type inference. But that seems not to be the case.
Any pointers or help with these two questions would be greatly appreciated.
Many thanks.