Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Generics F#泛型:我可以将一个类型约束为“非”;“单位”是多少;?_Generics_F#_Constraints - Fatal编程技术网

Generics F#泛型:我可以将一个类型约束为“非”;“单位”是多少;?

Generics F#泛型:我可以将一个类型约束为“非”;“单位”是多少;?,generics,f#,constraints,Generics,F#,Constraints,我目前正在自学F#,并多次遇到这种需求,所以我认为是时候得到答案了 基本上,我希望函数是泛型的,能够处理除单元之外的所有类型。在最近的一个例子中,我正在编写一个函数,它将包装任何其他函数并将其结果缓存在给定的并发字典中。我的代码如下: type CacheStore = System.Collections.Concurrent.ConcurrentDictionary<Guid * obj, DateTime * obj> let (|ItemPresent|ItemExpire

我目前正在自学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)
type CacheStore=System.Collections.Concurrent.ConcurrentDictionary
let(| ItemPresent | ItemExpired | ItemNotPresent |)((cacheStore:cacheStore),cacheKey)=
如果cacheStore.ContainsKey cacheKey
然后
让expiryTime,value=cacheStore。[cacheKey]
如果expiryTime>DateTime.Now
然后是现值
else ItemExpired(DateTime.Now-expiretime)
其他项目不存在
let缓存(cacheStore:cacheStore)(函数缓存:'a->'b)=
让functionId=Guid.NewGuid()
让updateCache(cacheStore:cacheStore)cacheKey newValue=
printfn“更新缓存”
cacheStore.[cacheKey]
让cacheKey=functionId(框arg)
将cacheStore、cacheKey与
|项目现值->
printfn“从缓存中检索”
值:?>'b
|项目已过期->
printfn“缓存中的项但在%A前过期;正在运行函数”在%A前
updateCache cacheStore cacheKey(函数缓存参数)
|ItemNotPresent->
printfn“项不在缓存中;正在运行函数。”
updateCache cacheStore cacheKey(函数缓存参数)
显然,缓存一个void/returns unit函数是没有意义的,事实上,如果有人试图缓存一个'a->unit函数,这段代码在运行时会失败

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

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

没有好的方法来做到这一点,我建议不要尝试这样做<代码>单元只是一个具有单个值的类型(
()
)-为什么要以任何特殊方式对待这一类型?当与
单元
返回函数一起使用时,您的函数不会中断,因此,如果它不能提供任何好处,为什么不相信调用者不使用它呢?如果您的调用者试图以通用方式使用您的函数,那么您对函数施加的任何限制都会传播到他们身上,这也可能是不希望的。

感谢您尝试Daniel-我想在这种情况下,这是一种学术性的,但仍然很有趣。在此之前,我编写过高阶函数,在传递单元返回函数时会抛出异常,我很想知道将来是否可以阻止它。由于
unit
是一个普通类型(),我认为没有对类型arg施加过多约束的方法可以做到这一点。一个选项(可能限制太多)是在“b:struct时使用约束
'b。另一个选项是在“b:>System.Collections.isstructuraleQuatable
时使用约束
'b,这将适用于DU和记录。如果不严格定义可接受的类型,我看不到排除
unit
的方法。@Daniel现在有意思的是,您指向源代码并提到unit是一个普通类型。我看到它为对象的成员提供覆盖。。。那么为什么
().GetHashCode()
抛出一个NRE呢?你是对的,除了一个令人恼火的事实,
()
在运行时被实现为null。在我的代码的原始版本中,它有一个类似于
cache store f arg
的签名,我在某个时候调用了
arg.GetHashCode()
。如果您包装了一个单元函数,那么当您试图调用包装的版本时,您会得到一个NRE,因为
()。GetHashCode()
会抛出一个异常。令人沮丧。@Richiban:这很容易处理:
将arg与null匹配->0 | |->arg.GetHashCode()
。这种方法的好处是它处理更一般的空引用情况,而不是一次性的
unit
@Daniel我认为这不是一个好主意:
match arg with null->0 | |->arg.GetHashCode()
,因为这不会在我的缓存函数中添加“requires'a:null”约束吗?是的,对不起,装箱将避免约束:
match box arg with null->…
@Richiban:re:kvb的注释,值得注意的是
hash
执行结构化哈希,因此对于某些类型,您将观察到与
GetHashCode()
不同的行为,其中最明显的是数组。