Использование Span <T>в F #: Почему это не нормально? - PullRequest
3 голосов
/ 01 июня 2019

Я пытаюсь реализовать библиотеку двоичного синтаксического анализатора с использованием Span. Я не уверен, что это действительно хорошая идея, я просто хочу узнать больше об обоих.

Я уже писал небольшой двоичный парсер с использованием комбинаторов парсеров , который отлично работает.

Код выглядит так:

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 Ответ

6 голосов
/ 02 июня 2019

Этот шаблон (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 # используют распределение кучи.Это было бы противоречием, поэтому оно не поддерживается.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...