Search code examples
f#lazy-evaluationmutable

lazy() works for single string but not for a sequence of objects?


So I have a cache system for a single value with a type like this:

module CachedSessionToken = 

// returns a lazy value that initializes the cache when 
// accessed for the first time (safely)
let private createCacheInitialization() = 
    lazy(SomeLongRunningOperationThatReturnsAString())

// current cache represented as lazy value
let mutable private currentCache = createCacheInitialization()

// Reset - cache will be re-initialized next time it is accessed
// (this doesn't actually initialize a cache - just creates a lazy value)
let Reset() = currentCache <- createCacheInitialization()

let GetCache() =
    currentCache.Value

And this works.

However, when I try to do the same for something else (a sequence of objects), it seems that the value is never retained, it's all the time evaluated every time I request it, why? The code below:

module CachedLayout = 

let mutable private lastAccess:Option<DateTime> = None

// returns a lazy value that initializes the cache when 
// accessed for the first time (safely)
let private createCacheInitialization() = 
  lazy(
      seq {
        yield new SomeObject (".")
        yield new SomeObject ("..")

        let folderPaths = SomeLongRunningOperationThatReturnsArrayOfStrings()
        lastAccess <- Option.Some(DateTime.Now)
        for name in folderPaths
          do yield new SomeObject (name)

        let filePaths = SomeOtherLongRunningOperationThatReturnsArrayOfStrings()
        lastAccess <- Option.Some(DateTime.Now)
        for name in filePaths
          do yield new SomeObject (name)
      }
  )

// current cache represented as lazy value
let mutable private currentCache = createCacheInitialization()

// Reset - cache will be re-initialized next time it is accessed
// (this doesn't actually initialize a cache - just creates a lazy value)
let Reset() = currentCache <- createCacheInitialization()

let GetCache() =
    if (lastAccess.IsSome && DateTime.Now > (lastAccess.Value + TimeSpan.FromSeconds (10.0))) then
        Reset()
    currentCache.Value

Solution

  • Ok, I just realised why: the type that the lazy() thingy returns. If it's a sequence, it will be always evaluated because it's not a proper object that can be stored.

    I had to change it to this to make it work:

    // returns a lazy value that initializes the cache when 
    // accessed for the first time (safely)
    let private createCacheInitialization() = 
      lazy(
          new List<SomeObject>(seq {
              yield new SomeObject (".")
              yield new SomeObject ("..")
    
              let folderPaths = SomeLongRunningOperationThatReturnsArrayOfStrings()
              lastAccess <- Option.Some(DateTime.Now)
              for name in folderPaths
                  do yield new SomeObject (name)
    
              let filePaths = SomeOtherLongRunningOperationThatReturnsArrayOfStrings()
              lastAccess <- Option.Some(DateTime.Now)
              for name in filePaths
                  do yield new SomeObject (name)
          })
    )