F# Inline Function Specialization

602 Views Asked by At

My current project involves lexing and parsing script code, and as such I'm using fslex and fsyacc. Fslex LexBuffers can come in either LexBuffer<char> and LexBuffer<byte> varieties, and I'd like to have the option to use both.

In order to user both, I need a lexeme function of type ^buf -> string. Thus far, my attempts at specialization have looked like:

let inline lexeme (lexbuf: ^buf) : ^buf -> string where ^buf : (member Lexeme: char array) =
  new System.String(lexbuf.Lexeme)

let inline lexeme (lexbuf: ^buf) : ^buf -> string where ^buf : (member Lexeme: byte array) =
  System.Text.Encoding.UTF8.GetString(lexbuf.Lexeme)

I'm getting a type error stating that the function body should be of type ^buf -> string, but the inferred type is just string. Clearly, I'm doing something (majorly?) wrong.

Is what I'm attempting even possible in F#? If so, can someone point me to the proper path?

Thanks!

3

There are 3 best solutions below

1
On BEST ANSWER

Functions and members marked as inline cannot be overloaded, so your original strategy won't work. You need to write different code for both of the declarations, so you need to use overloading (if you want to write this without boxing and dynamic type tests).

If you're using standard F# tools, then the type you'll get as a buffer will always be LexBuffer<'T> and you want to have two overloads based on the type argument. In this case, you don't need the static member constraints at all and can write just:

type Utils = 
  static member lexeme(buf:LexBuffer<char>) = 
    new System.String(buf.Lexeme)
  static member lexeme(buf:LexBuffer<byte>) = 
    System.Text.Encoding.UTF8.GetString(buf.Lexeme)
0
On
type LexBuffer<'a>(data : 'a []) =
  member this.Lexeme = data

let lexeme (buf : LexBuffer<'a>) =
  match box buf.Lexeme with
  | :? (char array) as chArr ->
      new System.String(chArr)
  | :? (byte array) as byArr ->
      System.Text.Encoding.UTF8.GetString(byArr)
  | _ -> invalidArg "buf" "must be either char or byte LexBuffer"

new LexBuffer<byte>([| 97uy; 98uy; 99uy |])
|> lexeme
|> printfn "%A"

new LexBuffer<char>([| 'a'; 'b'; 'c' |])
|> lexeme
|> printfn "%A"
1
On

Are you sure this strategy of redefining inline functions with different argument types can work? Looks like you're trying to overload to me...