Pergunta

I'm currently teaching myself F#, and have come across this need a few times so I thought it was time to get an answer.

Essentially I would like a function to be generic and to work with every type except unit. In the most recent case I'm writing a function that will wrap any other function and cache its result in the given concurrent dictionary. My code so far is as follows:

type CacheStore = System.Collections.Concurrent.ConcurrentDictionary<Guid * obj, DateTime * obj>

let (|ItemPresent|ItemExpired|ItemNotPresent|) ((cacheStore : CacheStore), cacheKey) =
    if cacheStore.ContainsKey cacheKey
    then
        let expiryTime, value = cacheStore.[cacheKey]
        if expiryTime > DateTime.Now
        then ItemPresent value
        else ItemExpired (DateTime.Now - expiryTime)
    else ItemNotPresent

let cache<'a, 'b> (cacheStore : CacheStore) (functionToCache : 'a -> 'b) =
    let functionId = Guid.NewGuid()

    let updateCache (cacheStore : CacheStore) cacheKey newValue =
        printfn "Updating cache"
        cacheStore.[cacheKey] <- ((DateTime.Now.AddSeconds 1.0), box newValue)
        newValue

    fun arg ->
        let cacheKey = functionId, (box arg)

        match cacheStore, cacheKey with
        | ItemPresent value ->
            printfn "Retrieving from cache"
            value :?> 'b
        | ItemExpired ago ->
            printfn "Item in cache but expired %A ago; running function" ago
            updateCache cacheStore cacheKey (functionToCache arg)
        | ItemNotPresent ->
            printfn "Item not in cache; running function."
            updateCache cacheStore cacheKey (functionToCache arg)

Obviously, it makes no sense to cache a function that is void / returns unit, and in fact this code will fail at runtime if someone tries to cache a 'a -> unit function.

How do I make it fail at compiletime if someone calls this incorrectly? From what I've read I feel that I should be able to do this with an inline function and static type constraints but I don't know how to write that...

UPDATE I have since found that this particular version actually does work with unit functions - the cache wrapper essentially has no effect (it just adds runtime overhead), but the gist of the original question still stands: can I stop people calling this function with unit functions?

Foi útil?

Solução

There's no good way to do this, and I'd recommend against trying to. unit is just a type with a single value (()) - why treat this one type in any special way? Your function won't break when used with a unit returning function, so why not trust your callers not to use it if it doesn't provide any benefit? And if your callers are trying to use your function in a generic way, then any restriction you place on your function will propagate to them, which may also be undesired.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top