Search code examples
f#quotationstype-providers

Casting an untyped code quotation?


In the MiniCsvTypeProvider, I see the following line of code:

        let prop = ProvidedProperty(fieldName, fieldTy, GetterCode = fun [row] -> <@@ (%%row:float[]).[i] @@>)

with GetterCode type is : Quotations.Expr list -> Quotations.Expr :

I don't really know what the lambda fun is doing....

  • it matches its input with a single element array, binding it to a variable named 'row' , of type Quotations.Expr from the GetterCode signature.

  • it creates a code quotation in return

  • inside the code quotation, it uses %%row:float[] and I don't know what this means : is that a Float [] type constraint for untyped code quotations ?

Solution

  • Jack's answer is correct. I'll add a bit more context. (%%) is the untyped splice operator (that is, it splices a Quotations.Expr into another typed or untyped quotation), while (%) is the typed splice operator (that is, it splices a Quotations.Expr<'t> for some 't into another typed or untyped quotation). (a : ty) is just a type annotation, so (%%row : float[]) indicates that when row is spliced into the quotation the result is a float[]. Without this annotation, %%row could be a value of any type, and the compiler would be unable to infer what we mean by the .[] indexer (just as it can't infer the type of arr in fun arr i -> arr.[i]).

    In case it's helpful, here are some alternative ways to express roughly the same thing as <@@ (%%row:float[]).[i] @@>:

    • We can convert the untyped quotation to a typed quotation before splicing:

      let typedRow = Quotations.Expr.Cast<float[]> row 
      <@@ %typedRow.[i] @@>
      

      Here we are using the typed splice operator (%), so the compiler knows that %typedRow is a float[] and that the .[] operator is applicable.

    • We can use a different way of indexing into the array so that F#'s type inference can determine the type of %%row without an annotation:

      <@@ Array.get %%row i : float @@>
      

      Here, the Array.get method takes an 'a[] as an argument and we add a type annotation which indicates that the result is a float, so F# will infer that %%row is a float[].