I am trying to implement a binary parser combinator library using Span. I am not sure if this is actually a good idea I just want to learn more about both.
I have already written a small binary parser a while ago using parser combinators which works perfectly.
The code looks like this:
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
...
I tried implemented it using Span instead of ByteRange and I just cannot do it.
Here is what I tried:
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
I get following error in the map function in the line match parser bytes with:
error FS0418: The byref typed value 'bytes' cannot be used at this point
What does this mean? Why can't I use Span here? Has somebody tried to implement parser combinator with Span? How would you try to solve this?
Thanks in advance.
This pattern (Span or other byref-like structs as a higher-order function parameter) is not supported:
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
A simpler form:
let foo (f: Span<int> -> int) (x: Span<int>) = f x
Gives an error on f as well. There are some slight quirks with byref-like types and type abbreviations that are hiding the error on parse but you'll see it if you give it an explicit signature.
The reason is that a byref-like struct is only allocated on the stack. However, higher-order functions in F# use heap allocation. This would be a contradiction, so it's not supported.
Related
In the F# core libraries there are functions whose signature seemingly changes based on the parameter at compile-time:
> sprintf "Hello %i" ;;
val it : (int -> string) = <fun:it#1>
> sprintf "Hello %s" ;;
val it : (string -> string) = <fun:it#2-1>
Is it possible to implement my own functions that have this property?
For example, could I design a function that matches strings with variable components:
matchPath "/products/:string/:string" (fun (category : string) (sku : string) -> ())
matchPath "/tickets/:int" (fun (id : int) -> ())
Ideally, I would like to do avoid dynamic casts.
There are two relevant F# features that make it possible to do something like this.
Printf format strings. The compiler handles format strings like "hi %s" in a special way. They are not limited just to printf and it's possible to use those in your library in a somewhat different way. This does not let you change the syntax, but if you were happy to specify your paths using e.g. "/products/%s/%d", then you could use this. The Giraffe library defines routef function, which uses this trick for request routing:
let webApp =
choose [
routef "/foo/%s/%s/%i" fooHandler
routef "/bar/%O" (fun guid -> text (guid.ToString()))
]
Type providers. Another option is to use F# type providers. With parameterized type providers, you can write a type that is parameterized by a literal string and has members with types that are generated by some F# code you write based on the literal string parameter. An example is the Regex type provider:
type TempRegex = Regex< #"^(?<Temperature>[\d\.]+)\s*°C$", noMethodPrefix = true >
TempRegex().Match("21.3°C").Temperature.TryValue
Here, the regular expression on the first line is static parameter of the Regex type provider. The type provider generates a Match method which returns an object with properties like Temperature that are based on the literal string. You would likely be able to use this and write something like:
MatchPath<"/products/:category/:sku">.Match(fun r ->
printfn "Got category %s and sku %s" r.Category r.Sku)
I tweaked your example so that r is an object with properties that have names matching to those in the string, but you could use a lambda with multiple parameters too. Although, if you wanted to specify types of those matches, you might need a fancier syntax like "/product/[category:int]/[sku:string]" - this is just a string you have to parse in the type provider, so it's completely up to you.
1st: Tomas's answer is the right answer.
But ... I had the same question.
And while I could understand it conceptually as "it has to be 'the string format thing' or 'the provider stuff'"
I could not tell my self that I got until I tried an implementation
... And it took me a bit .
I used FSharp.Core's printfs and Giraffe's FormatExpressions.fs as guidelines
And came up with this naive gist/implementation, inspired by Giraffe FormatExpressions.fs
BTW The trick is in this bit of magic fun (format: PrintfFormat<_, _, _, _, 'T>) (handle: 'T -> 'R)
open System.Text.RegularExpressions
// convert format pattern to Regex Pattern
let rec toRegexPattern =
function
| '%' :: c :: tail ->
match c with
| 'i' ->
let x, rest = toRegexPattern tail
"(\d+)" + x, rest
| 's' ->
let x, rest = toRegexPattern tail
"(\w+)" + x, rest
| x ->
failwithf "'%%%c' is Not Implemented\n" x
| c :: tail ->
let x, rest = toRegexPattern tail
let r = c.ToString() |> Regex.Escape
r + x, rest
| [] -> "", []
// Handler Factory
let inline Handler (format: PrintfFormat<_, _, _, _, 'T>) (handle: 'T -> string) (decode: string list -> 'T) =
format.Value.ToCharArray()
|> List.ofArray
|> toRegexPattern
|> fst, handle, decode
// Active Patterns
let (|RegexMatch|_|) pattern input =
let m = Regex.Match(input, pattern)
if m.Success then
let values =
[ for g in Regex(pattern).Match(input).Groups do
if g.Success && g.Name <> "0" then yield g.Value ]
Some values
else
None
let getPattern (pattern, _, _) = pattern
let gethandler (_, handle, _) = handle
let getDecoder (_, _, decode) = decode
let Router path =
let route1 =
Handler "/xyz/%s/%i"
(fun (category, id) ->
// process request
sprintf "handled: route1: %s/%i" category id)
(fun values ->
// convert matches
values |> List.item 0,
values
|> List.item 1
|> int32)
let route2 =
Handler "/xyz/%i"
(fun (id) -> sprintf "handled: route2: id: %i" id) // handle
(fun values -> values|> List.head |> int32) // decode
// Router
(match path with
| RegexMatch (getPattern route2) values ->
values
|> getDecoder route2
|> gethandler route2
| RegexMatch (getPattern route1) values ->
values
|> getDecoder route1
|> gethandler route1
| _ -> failwith "No Match")
|> printf "routed: %A\n"
let main argv =
try
let arg = argv |> Array.skip 1 |> Array.head
Router arg
0 // return an integer exit code
with
| Failure msg ->
eprintf "Error: %s\n" msg
-1
I am trying to write a Monad in F# but I can not compile the code and I am getting error FS0001
error: This expression was expected to have type 'Result' but here has type '(Result<'a> -> Result<'b>) -> Result<'b>'
open System
type Result<'TSuccess> =
| Success of 'TSuccess
| Failure
let bind x f =
match x with
| Success x -> f (Success x)
| Failure -> Failure
let stringToInt (s:string) =
try
let result = s |> int
Success result
with
|_-> Failure
let isPositive (i:int) =
if ( i > 0) then Success i : Result<int>
else Failure
let toString (i:int) =
try
let result = i |> string
Success result
with
|_ -> Failure
let bindIsPositive = bind isPositive : Result<int>
let bindToString = bind toString : Result<string>
let (>>=) x f = bind f x
let strintToIntIsPositiveIntToString s = stringToInt >>= bindIsPositive >>= bindToString
[<EntryPoint>]
let main argv =
printfn "10"
let mys = strintToIntIsPositiveIntToString "9"
Console.WriteLine mys.ToString
0 // return an integer exit code
First of all, the type of your bind is not right:
your version : Result<'a> -> (Result<'a> -> Result<'b>) -> Result<'b>
typical type : Result<'a> -> ('a -> Result<'b>) -> Result<'b>
It will also be a lot easier to do the rest if you switch the order of parameters to get:
bind : ('a -> Result<'b>) -> Result<'a> -> Result<'b>
So, you can use the following bind:
let bind f x =
match x with
| Success x -> f x
| Failure -> Failure
Once you do this, you can define bindIsPositive and bindToString. The bind operation now takes a function as a first argument, so this works but you have to remove your type annotation:
let bindIsPositive = bind isPositive
let bindToString = bind toString
When composing functions, you can then either use your >>= operator, or use normal F# piping and bind functions:
let strintToIntIsPositiveIntToString x = x |> stringToInt |> bindIsPositive |> bindToString
let strintToIntIsPositiveIntToString x = x >>= stringToInt >>= isPositive >>= toString
Why is f inferred to be of type obj -> int and not seq<'T> -> int ?
let fseq (o:'T seq) : int = 0 //val fseq : o:seq<'T> -> int
let f = match true with //f : obj -> int
| true -> fun o -> fseq o
| false -> failwith ""
That leads to strange message such as the following later
let a : (obj -> int ) list = []
let a' = f::a //error yet type inference says f : obj -> int
ps : not really a problem, inference always has a limit and it is quite powerful in fsharp, but I am wondering why the type would be unified to obj in here.
The first error is a typical value restriction.
In f# functions can be generic, not constants. You can solve it as any other value restriction, for example moving the argument to the left side of the =:
let f o = match true with
| true -> fseq o
| false -> failwith ""
Regarding the second error it's confusing what you see. The code is not compiling because of the first error but intellisense is telling you another thing.
It's normal, specially when the code is not compiling that intellisense tells you something else, sometimes it infers the correct type but not the compiler, they are not always in sync.
The problem is with second part of Your code - a is (obj -> int ) list while it should be (obj seq -> int ) since it is type of fseq function. The following codes works well.
let fseq (o : 'T seq) : int = 0
let f = match true with
| true -> fun o -> fseq o
| false -> failwith ""
let a : (obj seq -> int ) list = []
let a' = f::a
Can someone explain why compiler is giving me this error
Type mismatch. Expecting a
'a [] -> string
but given a
'a [] -> 'a []
The type 'string' does not match the type ''a []'
on this code snippet:
let rotate s: string =
[|for c in s -> c|]
|> Array.permute (function | 0 -> (s.Length-1) | i -> i-1)
while the one below compiles just fine:
let s = "string"
[|for c in s -> c|]
|> Array.permute (function | 0 -> (s.Length-1) | i -> i-1)
Your first snippet defines function rotate with return type of string.
Try to change it to:
let rotate (s: string) =
[|for c in s -> c|]
|> Array.permute (function | 0 -> (s.Length-1) | i -> i-1)
In this form you define a function with one string argument (I suppose that's what you wanted) and inferred return type.
Here is my code:
open System
let rec gcd a b =
match b with
| x when x = 0 -> a
| _ -> gcd(b, a % b)
let result = gcd 15 10
[<EntryPoint>]
let main(args : string[]) =
printfn "result = %d" result
0
Why I get the error with this code:
D:\datahub\Dropbox\development\myprojects\project-euler\Problem_5\problem_5.fs(6,16): error FS0001: Type mismatch. Expec
ting a
'a
but given a
int -> 'a
The resulting type would be infinite when unifying ''a' and 'int -> 'a'
The example tries to separate arguments by using a comma. In F# multiple arguments are supplied to a function by separating them with whitespace:
let rec gcd a b =
match b with
| x when x = 0 -> a
| _ -> gcd b (a % b)