patternMinor
F# cache manager
Viewed 0 times
managercachestackoverflow
Problem
This is a port of a C# cache manager we use to F#. This is my first F# code, and I've tried to make it as idiomatic as possible. I would appreciate input on style, performance considerations,correctness, anything at all. The goal is to learn more about F# and functional programming. The purpose of the code is to abstract away the IIS cache, and make it possible to use multiple levels of cache, such as using Redis as a second level cache.
```
namespace FSharpTools
open System.Web
open System.Web.Caching
open System.Collections
open System.Web.Configuration
open Microsoft.FSharp.Reflection
module TheOneCache =
type private CacheType = Web | Redis
//The ICache interface, which can be implemented for IIS cache, Redis, etc
type private ICache =
abstract member Add: string -> 'T -> unit
abstract member AddForTime: string -> 'T -> System.TimeSpan -> unit
abstract member Get: string -> 'T option
abstract member Exists: string -> bool
abstract member Remove: string -> unit
abstract member FlushAll: unit
abstract member FlushKeysThatStartWith: string -> unit
abstract member GetCountStartsWith: string -> int
abstract member GetCacheType: CacheType
//Implement the ICache interface as an IIS cache using an object expression
let private WebCache =
let GetHttpCacheEnum =
HttpRuntime.Cache
|> Seq.cast
let FilterKeys keyPrefix =
GetHttpCacheEnum
|> Seq.filter(fun e -> e.Key.ToString().StartsWith(keyPrefix))
{ new ICache with
member __.Add key value =
HttpRuntime.Cache.Insert(key, value, null, System.Web.Caching.Cache.NoAbsoluteExpiration, System.Web.Caching.Cache.NoSlidingExpiration, CacheItemPriority.Normal, null)
member __.AddForTime key value duration =
HttpRuntime.Cache.Insert(key, value, null, System.Web.Caching.Cache.NoAbsoluteExpiration, duration, CacheItemPriority.NotRemovable
```
namespace FSharpTools
open System.Web
open System.Web.Caching
open System.Collections
open System.Web.Configuration
open Microsoft.FSharp.Reflection
module TheOneCache =
type private CacheType = Web | Redis
//The ICache interface, which can be implemented for IIS cache, Redis, etc
type private ICache =
abstract member Add: string -> 'T -> unit
abstract member AddForTime: string -> 'T -> System.TimeSpan -> unit
abstract member Get: string -> 'T option
abstract member Exists: string -> bool
abstract member Remove: string -> unit
abstract member FlushAll: unit
abstract member FlushKeysThatStartWith: string -> unit
abstract member GetCountStartsWith: string -> int
abstract member GetCacheType: CacheType
//Implement the ICache interface as an IIS cache using an object expression
let private WebCache =
let GetHttpCacheEnum =
HttpRuntime.Cache
|> Seq.cast
let FilterKeys keyPrefix =
GetHttpCacheEnum
|> Seq.filter(fun e -> e.Key.ToString().StartsWith(keyPrefix))
{ new ICache with
member __.Add key value =
HttpRuntime.Cache.Insert(key, value, null, System.Web.Caching.Cache.NoAbsoluteExpiration, System.Web.Caching.Cache.NoSlidingExpiration, CacheItemPriority.Normal, null)
member __.AddForTime key value duration =
HttpRuntime.Cache.Insert(key, value, null, System.Web.Caching.Cache.NoAbsoluteExpiration, duration, CacheItemPriority.NotRemovable
Solution
My suggestion here is to just make a class for the multiple cache backed version that you are exposing. It can Implement
Eirik recently tweeted about this:
https://twitter.com/eiriktsarpalis/status/964067904164651008
If I hadn't suggested converting to a class, I'd also point out that normally, people try to do lowercase for module functions. You may think you have seen some counterexamples in FSharp.Core, but you are usually looking at Static class functions in that case, which tend to be upper case.
ICache, and add the other ethods you require. This will mean there are less function names to reason about, slightly less code, and will avoid module functions backed by stateful objects, which seem unidiomatic (subjective). The module functions as public APIs will also prevent you from adding optional parameters, passing a common parameter to all the functions if needed, and having overloaded functions, many of which tend to happen in a public API as needs change.Eirik recently tweeted about this:
https://twitter.com/eiriktsarpalis/status/964067904164651008
If I hadn't suggested converting to a class, I'd also point out that normally, people try to do lowercase for module functions. You may think you have seen some counterexamples in FSharp.Core, but you are usually looking at Static class functions in that case, which tend to be upper case.
Context
StackExchange Code Review Q#118643, answer score: 2
Revisions (0)
No revisions yet.