在F#中使用Span <t>:为什么这样做不行?

时间:2019-06-01 16:53:24

标签: f#

我正在尝试使用Span实现二进制解析器组合器库。我不确定这是否真的是个好主意,我只是想了解更多有关这两者的信息。

前段时间,我已经使用parser combinators编写了一个小型的二进制解析器,该解析器运行良好。

代码如下:

type ByteRange =
    { Bytes : byte array
      BeginIndex : int
      EndIndex : int }

type ParserError<'err> =
    | EndOfStream
    | FormatError of 'err

type Parser<'T, 'err> = Parser of (ByteRange -> Result<'T * ByteRange, ParserError<'err>>)

let succeed value = Parser <| fun bytes -> Ok(value, bytes)
let fail error = Parser <| fun _ -> Error error

let internal fromResult result =
    Parser <| fun bytes ->
        match result with
        | Ok value -> Ok(value, bytes)
        | Error error -> Error(FormatError error)

let internal map f (Parser parse) =
    Parser <| fun byteRange ->
        match parse byteRange with
        | Ok(value', state') -> Ok(f value', state')
        | Error error -> Error error
...

我尝试使用Span代替ByteRange来实现它,但我做不到。

这是我尝试过的:

module BinaryParser

open System
open System.Runtime.CompilerServices

type ParserError<'err> =
    | EndOfStream
    | FormatError of 'err

[<Struct; IsByRefLike>]
type Success<'a> = {
    Value: 'a
    Bytes: Span<byte>
}

[<Struct; IsByRefLike>]
type ParsingResult<'a, 'err> =
| Success of success:Success<'a>
| Failure of failure:ParserError<'err>

type Parser<'T, 'err> =
    Span<byte> -> ParsingResult<'T, ParserError<'err>>

let succeed (value: 'a) =
    fun (bytes: Span<byte>) ->
        Success { Value = value; Bytes = bytes }

let fail error =
    fun _ ->
        Failure error

let internal fromResult result =
    fun bytes ->
        match result with
        | Ok value -> Success { Value = value; Bytes = bytes }
        | Error error -> Failure (FormatError error)

let internal map f (parse: Parser<_, _>) =
    fun bytes ->
        match parse bytes with
        | Success { Value = value'; Bytes = bytes' } -> Success { Value = f value'; Bytes = bytes' }
        | Failure error -> Failure error

我在map行的match parser bytes with函数中收到以下错误: 错误FS0418:此时无法使用byref类型的值'bytes'

这是什么意思?为什么我不能在这里使用Span?有人尝试过使用Span实现解析器组合器吗?您将如何解决这个问题?

谢谢。

1 个答案:

答案 0 :(得分:0)

不支持这种模式(Span或其他类似byref的结构作为高阶函数参数):

let internal map f (parse: Parser<_, _>) =
    fun bytes ->
        match parse bytes with
        | Success { Value = value'; Bytes = bytes' } -> Success { Value = f value'; Bytes = bytes' }
        | Failure error -> Failure error

一种简单的形式:

let foo (f: Span<int> -> int) (x: Span<int>) = f x

也给出f的错误。有一些与byref相似的类型和类型缩写的细微之处,它们掩盖了parse上的错误,但是如果给它一个明确的签名,您会看到它。

原因是类似byref的结构仅分配在堆栈上。但是,F#中的高阶函数使用堆分配。这将是一个矛盾,因此不支持。