如何在 F# 中使这些函数通用?

how to make these functions generic, in F#?

我有这两个功能:

// load from cache
let private loadFromCacheAsync filespec =
    async {
        let! bytes = File.ReadAllBytesAsync(filespec) |> Async.AwaitTask
        let result = 
            use pBytes = fixed bytes
            let sourceSpan = Span<byte>(NativePtr.toVoidPtr pBytes, bytes.Length) 
            MemoryMarshal.Cast<byte, ShortTradeData>(sourceSpan).ToArray()
        return (result |> Array.map (fun x -> x.ToTradeData())) 
    }    

// save to cache
let private saveToCacheAsync filespec (data: TradeData array) =
    Directory.CreateDirectory cacheFolder |> ignore
    let sizeStruct = sizeof<ShortTradeData>
    let smallData = data |> Array.map ShortTradeData.FromTradeData
    use ptr = fixed smallData
    let nativeSpan = Span<byte>(NativePtr.toVoidPtr ptr, data.Length * sizeStruct).ToArray()
    File.WriteAllBytesAsync(filespec, nativeSpan) |> Async.AwaitTask

我正在努力使它们通用:

// load from cache
let private loadFromCacheAsync<'a> filespec =
    async {
        let! bytes = File.ReadAllBytesAsync(filespec) |> Async.AwaitTask
        let result = 
            use pBytes = fixed bytes
            let sourceSpan = Span<byte>(NativePtr.toVoidPtr pBytes, bytes.Length) 
            MemoryMarshal.Cast<byte, 'a>(sourceSpan).ToArray()
        return result
    }    

// save to cache
let private saveToCacheAsync<'a> filespec (data: 'a array) =
    Directory.CreateDirectory cacheFolder |> ignore
    let sizeStruct = sizeof<'a>
    use ptr = fixed data
    let nativeSpan = Span<byte>(NativePtr.toVoidPtr ptr, data.Length * sizeStruct).ToArray()
    File.WriteAllBytesAsync(filespec, nativeSpan) |> Async.AwaitTask

第一个编译失败:

“没有已知的方法 Cast 重载”

第二个编译失败:

“当'a: unmanaged'

时,类型参数缺少约束

所以我可以通过改变函数使第二个编译:

let private saveToCacheAsync<'a when 'a: unmanaged> filespec (data: 'a array) =

但我认为“when 'a: struct”更有意义。阅读文档后,看起来非托管适用于具有原始(非托管)类型的结构......仍然不知道为什么结构不起作用。

但是对于第一个功能,我不知道应该做些什么来修复它。

完整的错误信息是:

  DataCache.fs(28, 17): [FS0041] No overloads match for method 'Cast'.
Known type of argument: Span<byte>
Available overloads:
 - MemoryMarshal.Cast<'TFrom,'TTo when 'TFrom: (new: unit -> 'TFrom) and 'TFrom: struct and 'TFrom :> ValueType and 'TTo: (new: unit -> 'TTo) and 'TTo: struct and 'TTo :> ValueType>(span: ReadOnlySpan<'TFrom>) : ReadOnlySpan<'TTo>
 - MemoryMarshal.Cast<'TFrom,'TTo when 'TFrom: (new: unit -> 'TFrom) and 'TFrom: struct and 'TFrom :> ValueType and 'TTo: (new: unit -> 'TTo) and 'TTo: struct and 'TTo :> ValueType>(span: Span<'TFrom>) : Span<'TTo>

如果我看一下它需要的类型:

我们在这里关心TTo:

我还是不明白:看起来它需要是一个结构,并且可以转换为值类型(那么是结构?)并且有一个空的构造函数?第一行没看懂

由于 loadFromCacheAsync 的“from”类型参数始终是 byte,您只需要对其称为 'a 的“to”参数进行三个约束:

  • 'a: (new: unit -> 'a)
  • 'a: struct
  • 'a :> System.ValueType

最后两个看起来确实多余,但我认为这只是 .NET 基本要求的产物。

loadFromCacheAsync 的最终签名是:

let private loadFromCacheAsync<'a when 'a: (new: unit -> 'a) and 'a: struct and 'a :> ValueType> filespec =