F#泛型:我可以将类型约束为不是“单位”吗?

时间:2014-04-04 15:06:50

标签: generics f# constraints

我目前正在教自己F#,并且已经遇到过这种需求几次,所以我认为是时候回答。

基本上我希望一个函数是通用的,并且可以处理除之外的所有类型。在最近的情况下,我正在编写一个函数,它将包装任何其他函数并将其结果缓存在给定的并发字典中。到目前为止,我的代码如下:

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)

显然,缓存一个void / return unit,的函数是没有意义的,事实上如果有人试图缓存一个&#39; a - &gt;这个代码将在运行时失败。单位功能。

如果有人不正确地调用它,如何在编译时使其失败?从我所读过的内容来看,我觉得我应该能够通过内联函数和静态类型约束来实现这一点,但我不知道如何编写它......

更新 我后来发现这个特定的版本实际上 使用单元函数 - 缓存包装器基本上没有效果(它只是增加了运行时开销),但原始问题的要点仍然存在:我可以停止人们用单位函数调用这个函数?

1 个答案:

答案 0 :(得分:3)

没有好办法做到这一点,我建议不要尝试。 unit只是一个具有单个值(())的类型 - 为什么要以任何特殊方式处理这一类型?当与unit返回功能一起使用时,您的功能不会中断,那么为什么不相信您的呼叫者如果不提供任何好处就不使用它?如果您的调用者试图以通用方式使用您的函数,那么您对函数的任何限制都将传播给它们,这也可能是不受欢迎的。