In general, I'd recommend using typed quotations whenever you can. As usual, the types will allow you to statically enforce some correctness conditions that might otherwise cause failures at runtime. Consider:
let one = <@@ "one" @@>
// exception at runtime
let two = <@@ 1 + %%one @@>
as opposed to
let one = <@ "one" @>
// compile time error: the type 'string' does not match the type 'int'
let two = <@ 1 + %one @>
Additionally, sometimes untyped quotations need additional type annotations in cases where typed quotations don't:
// ok
let l = <@ [1] @>
let l2 = <@ List.map id %l @>
// fails at runtime (obj list assumed instead of int list)
let l = <@@ [1] @@>
let l2 = <@@ List.map id %%l @@>
// ok
let l = <@@ [1] @@>
let l2 = <@@ List.map (id:int->int) %%l @@>
However, if you're building something extremely generic out of quotations, it may not be possible to use typed quotations (e.g. because the types aren't statically known). In that sense, untyped quotations give you more flexibility.
Also note that it's extremely easy to convert between typed and untyped quotations as needed (upcast an Expr<_>
to Expr
to go from typed to untyped; use Expr.Cast
to go the other way).