[JS/TS] Add F# quotation support with serializable QuotExpr AST#4474
[JS/TS] Add F# quotation support with serializable QuotExpr AST#4474OnurGumus wants to merge 2 commits intofable-compiler:mainfrom
Conversation
Add support for F# code quotations (<@ expr @> and <@@ expr @@>) targeting JS/TS. Quotations compile to a serializable QuotExpr discriminated union that works with Thoth.Json and Fable.Remoting for cross-platform serialization between Fable clients and .NET backends. Supported quotation nodes: values (int, float, string, char, bool, unit), lambdas, let bindings, if/then/else, function calls, operators, tuples, unions, records, options, lists, sequential, variable set, field get/set. The % splice operator is supported for composing quotations. New files: - Fable.Core.Quotations.fs: shared F# DU types for .NET deserialization - Quotation.ts: JS/TS runtime with proper Union subclasses - QuotationEmitter.fs: compiler transform from Fable Quote AST to runtime calls - QuotationTests.fs: test suite Other targets (Python, Rust, Dart, Beam, PHP) have stubs with error messages. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Nice! We should coordinate / align this with #4474 |
| | Unit -> "Microsoft.FSharp.Core.Unit" | ||
| | Any -> "System.Object" | ||
| | LambdaType(argType, returnType) -> | ||
| $"Microsoft.FSharp.Core.FSharpFunc`2[{typeToString argType},{typeToString returnType}]" |
Check warning
Code scanning / Ionide.Analyzers.Cli
Warns about missing type specifiers in interpolated strings Warning
| | Unit -> "Microsoft.FSharp.Core.Unit" | ||
| | Any -> "System.Object" | ||
| | LambdaType(argType, returnType) -> | ||
| $"Microsoft.FSharp.Core.FSharpFunc`2[{typeToString argType},{typeToString returnType}]" |
Check warning
Code scanning / Ionide.Analyzers.Cli
Warns about missing type specifiers in interpolated strings Warning
| $"Microsoft.FSharp.Core.FSharpFunc`2[{typeToString argType},{typeToString returnType}]" | ||
| | Tuple(genArgs, _) -> | ||
| let args = genArgs |> List.map typeToString |> String.concat "," | ||
| $"System.Tuple`{genArgs.Length}[{args}]" |
Check warning
Code scanning / Ionide.Analyzers.Cli
Warns about missing type specifiers in interpolated strings Warning
| $"Microsoft.FSharp.Core.FSharpFunc`2[{typeToString argType},{typeToString returnType}]" | ||
| | Tuple(genArgs, _) -> | ||
| let args = genArgs |> List.map typeToString |> String.concat "," | ||
| $"System.Tuple`{genArgs.Length}[{args}]" |
Check warning
Code scanning / Ionide.Analyzers.Cli
Warns about missing type specifiers in interpolated strings Warning
| let args = genArgs |> List.map typeToString |> String.concat "," | ||
| $"System.Tuple`{genArgs.Length}[{args}]" | ||
| | DeclaredType(entRef, _genArgs) -> entRef.FullName | ||
| | Option(genArg, _) -> $"Microsoft.FSharp.Core.FSharpOption`1[{typeToString genArg}]" |
Check warning
Code scanning / Ionide.Analyzers.Cli
Warns about missing type specifiers in interpolated strings Warning
| $"System.Tuple`{genArgs.Length}[{args}]" | ||
| | DeclaredType(entRef, _genArgs) -> entRef.FullName | ||
| | Option(genArg, _) -> $"Microsoft.FSharp.Core.FSharpOption`1[{typeToString genArg}]" | ||
| | List genArg -> $"Microsoft.FSharp.Collections.FSharpList`1[{typeToString genArg}]" |
Check warning
Code scanning / Ionide.Analyzers.Cli
Warns about missing type specifiers in interpolated strings Warning
| | DeclaredType(entRef, _genArgs) -> entRef.FullName | ||
| | Option(genArg, _) -> $"Microsoft.FSharp.Core.FSharpOption`1[{typeToString genArg}]" | ||
| | List genArg -> $"Microsoft.FSharp.Collections.FSharpList`1[{typeToString genArg}]" | ||
| | Array(genArg, _) -> $"{typeToString genArg}[]" |
Check warning
Code scanning / Ionide.Analyzers.Cli
Warns about missing type specifiers in interpolated strings Warning
| Helper.LibCall(com, "Quotation", "mkValue", Any, [ lit ]) | ||
|
|
||
| and private emitUnsupported (com: Compiler) (nodeName: string) : Expr = | ||
| let msg = $"Unsupported quotation node: {nodeName}" |
Check warning
Code scanning / Ionide.Analyzers.Cli
Warns about missing type specifiers in interpolated strings Warning
Rename to match #4474 conventions so it can rebase cleanly: - mkVarExpr -> mkVar (Expr.Var), mkVar -> mkQuotVar (Var constructor) - mkApp -> mkApplication - isNewUnion -> isNewUnionCase Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use "quotation" as the canonical module name: - Python: quotation.py (renamed from fable_quotation.py) - Beam: fable_quotation.erl (fable_ prefix added by getLibPath) - JS (#4474): Quotation.ts (natural mapping) Revert the Beam getLibPath workaround as it's no longer needed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
I have fixed #4398 so that the common code will work for JS/TS as well. Thus can be reused by this PR, so most changes to Replacements.fs will not be needed. @MangelMaxime @ncave. If we are good with adding Quotations then I can merge my PR? Then this PR will be much simpler on top of that. |
Add QuotationEmitter.fs to Fable.Standalone.fsproj and add type-safe format specifiers to interpolated strings in QuotationEmitter.fs.
|
@dbrattli by all means that's fine by me. probably better as my pr only targets js/ts as of now. |
|
@OnurGumus Ok, let us just check with @MangelMaxime that we are ok with the Fable.AST change at this point since it may break extensions. |
Summary
<@ expr @>and<@@ expr @@>) targeting JS/TSQuotExprdiscriminated union (defined inFable.Core.Quotations)%splice operator is supported for composing quotationsSupported quotation nodes
Values (int, float, string, char, bool, unit, null), lambdas, let/letrec bindings, if/then/else, function calls, operators, tuples, unions, records, options, lists, sequential, variable set, field get/set
New files
src/Fable.Core/Fable.Core.Quotations.fsQuotExpr,QuotVar,QuotLiteral) for .NET deserializationsrc/fable-library-ts/Quotation.tsquotExprFromJSONdeserializersrc/Fable.Transforms/QuotationEmitter.fsQuoteAST → runtime constructor callstests/Js/Main/QuotationTests.fsUsage
Test plan
%) tests verify expression composition🤖 Generated with Claude Code