Search code examples
functional-programmingf#abstract-classsubclassmemoization

F# Subclasses and Memoization


I am struggling to get memoization to work when the memoized function is an abstract function which is overridden/defined within a subclass rather than the parent class.

When the memoized function is defined in the parent class, it works fine. When I define the signature of the memoized function in the parent class, and then override it in the subclass, I can't figure out the appropriate syntax.

open System
    
let Memoize f =
    let dict = Dictionary<_, _>()
    fun c ->
        let exists, value = dict.TryGetValue c
        match exists with
        | true -> value
        | _ -> 
            let value = f c
            dict.Add(c, value)
            value

[<AbstractClass>]
type ParentClass() as this = 
    let someparam = DateTime(2022,1,1)
    
    let SlowNumber100InParentClass(t) =
        System.Threading.Thread.Sleep(1000)
        100.0

    member val MemoParentClassSlow100 = Memoize SlowNumber100InParentClass
    member this.MultiplyBy2A = (this.MemoParentClassSlow100 someparam) * 2.0

    abstract MemoSubClassSlow100: DateTime->float
    member this.MultiplyBy2B = (this.MemoSubClassSlow100 someparam) * 2.0

type MyClass() as this = 
    inherit ParentClass()

    let SlowNumber100InSubClass(t) = 
         System.Threading.Thread.Sleep(1000)
         100.0

    override this.MemoSubClassSlow100(t) = Memoize SlowNumber100InSubClass t        // doesn't "memoize"
    //override val MemoSubClassSlow100 = Memoize SlowNumber100InSubClass            // This feels intuitive to me, but error is "No abstract property was found that corresponds to this override"
    // ??? somehow else ???
    
    
[<EntryPoint>]
let main args = 
    let x = new MyClass()
    for i in 1..10 do
        Console.WriteLine(x.MultiplyBy2A)                                     // this is fast

    for i in 1..10 do
        Console.WriteLine(x.MultiplyBy2B)                                     // this is slow
    0

Solution

  • You're very close. Just use member val in the subclass to hold the memoized function, since that member doesn't override anything in the parent class:

    type MyClass() =
        inherit ParentClass()
    
        let SlowNumber100InSubClass(t) = 
             System.Threading.Thread.Sleep(1000)
             100.0
    
        member val MemoSubClassSlow100_ = Memoize SlowNumber100InSubClass
        override this.MemoSubClassSlow100(t) = this.MemoSubClassSlow100_ t
    

    Personally, I think let is usually preferable to member val, though:

        let memoSubClassSlow100 = Memoize SlowNumber100InSubClass
        override _.MemoSubClassSlow100(t) = memoSubClassSlow100 t