This is an automated email from the ASF dual-hosted git repository. chaokunyang pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/fory-site.git
commit c5318149784d0b1283d69438196fae4f4ba95f6b Author: chaokunyang <[email protected]> AuthorDate: Fri May 15 11:18:15 2026 +0000 🔄 synced local 'docs/compiler/' with remote 'docs/compiler/' --- docs/compiler/compiler-guide.md | 25 +++++++++ docs/compiler/generated-code.md | 115 ++++++++++++++++++++++++++++++++++++++++ docs/compiler/index.md | 15 +++--- docs/compiler/schema-idl.md | 41 ++++++++------ 4 files changed, 172 insertions(+), 24 deletions(-) diff --git a/docs/compiler/compiler-guide.md b/docs/compiler/compiler-guide.md index 28ed04f1f1..a1c35e0268 100644 --- a/docs/compiler/compiler-guide.md +++ b/docs/compiler/compiler-guide.md @@ -67,6 +67,7 @@ Compile options: | `--javascript_out=DST_DIR` | Generate JavaScript code in DST_DIR | (none) | | `--swift_out=DST_DIR` | Generate Swift code in DST_DIR | (none) | | `--dart_out=DST_DIR` | Generate Dart code in DST_DIR | (none) | +| `--scala_out=DST_DIR` | Generate Scala 3 code in DST_DIR | (none) | | `--go_nested_type_style` | Go nested type naming: `camelcase` or `underscore` | `underscore` | | `--swift_namespace_style` | Swift namespace style: `enum` or `flatten` | `enum` | | `--emit-fdl` | Emit translated FDL (for non-FDL inputs) | `false` | @@ -174,6 +175,9 @@ foryc schema.fdl --java_out=./java/gen --python_out=./python/src --go_out=./go/g # Combine with import paths foryc schema.fdl --java_out=./gen/java -I proto/ -I common/ + +# Generate Scala 3 code to a specific directory +foryc schema.fdl --scala_out=./src/main/scala ``` When using `--{lang}_out` options: @@ -250,6 +254,7 @@ Compiling src/main.fdl... | JavaScript | `javascript` | `.ts` | Interfaces with registration function | | Swift | `swift` | `.swift` | Fory Swift model macros | | Dart | `dart` | `.dart` | `@ForyStruct` classes with annotations | +| Scala | `scala` | `.scala` | Scala 3 models with macro derivation | ## Output Structure @@ -379,6 +384,26 @@ generated/ - Registration helper class included in the part file - Typed arrays used for non-optional, non-ref primitive lists (e.g., `Int32List`) +### Scala + +``` +generated/ +└── scala/ + └── example/ + ├── User.scala + ├── Status.scala + ├── Animal.scala + └── ExampleForyRegistration.scala +``` + +- One Scala 3 source file per generated type +- Package structure matches the Fory IDL package +- Messages derive `org.apache.fory.scala.ForySerializer` +- `optional T` fields use `Option[T]` +- Enums use Scala 3 `enum` +- Unions use Scala 3 ADT `enum` with `@ForyUnion`, `@ForyCase`, and an `UnknownCase` +- Registration helper object included + ### C# IDL Matrix Verification Run the end-to-end C# IDL matrix (FDL/IDL/Proto/FBS generation plus roundtrip tests): diff --git a/docs/compiler/generated-code.md b/docs/compiler/generated-code.md index e3a8f9b7eb..723a4eb4cf 100644 --- a/docs/compiler/generated-code.md +++ b/docs/compiler/generated-code.md @@ -1042,6 +1042,121 @@ void main() { } ``` +## Scala + +The Scala target emits Scala 3 source only. The `fory-scala` runtime artifact +still supports Scala 2.13 and Scala 3, but generated IDL source and macro +derivation require Scala 3. + +### Output Layout + +For `package addressbook`, Scala output is generated under: + +- `<scala_out>/addressbook/` +- Type files: `AddressBook.scala`, `Person.scala`, `Dog.scala`, `Cat.scala`, `Animal.scala` +- Registration helper: `AddressbookForyRegistration.scala` + +### Type Generation + +Messages outside compiler-detected construction cycles generate case classes: + +```scala +import org.apache.fory.annotation.{ForyField, ForyStruct} +import org.apache.fory.scala.ForySerializer + +@ForyStruct +final case class Person( + @ForyField(id = 1) name: String, + @ForyField(id = 3) email: Option[String], + @ForyField(id = 7) phones: List[Person.PhoneNumber], + @ForyField(id = 8) pet: Animal +) derives ForySerializer +``` + +Messages in circular construction cycles generate normal classes with mutable +serialized fields so reads can register the object before reading back-references: + +```scala +import org.apache.fory.annotation.{ForyField, ForyStruct, Ref} +import org.apache.fory.scala.ForySerializer + +@ForyStruct +final class Node() derives ForySerializer { + @ForyField(id = 1) + var id: String = "" + + @Ref + @ForyField(id = 2) + var parent: Option[Node @Ref] = None +} +``` + +Enums generate Scala 3 enums with stable Fory IDs: + +```scala +import org.apache.fory.annotation.ForyEnumId + +enum PhoneType { + @ForyEnumId(0) + case Mobile + + @ForyEnumId(1) + case Home + + @ForyEnumId(2) + case Work +} +``` + +Unions generate Scala 3 ADT enums. Case ID `0` is reserved for the unknown-case +carrier; schema-defined cases start at `1`. + +```scala +import org.apache.fory.annotation.{ForyCase, ForyUnion} +import org.apache.fory.scala.ForySerializer + +@ForyUnion +enum Animal derives ForySerializer { + @ForyCase(id = 0) + case UnknownCase(caseId: Int, value: Any) + + @ForyCase(id = 1) + case DogCase(value: Dog) + + @ForyCase(id = 2) + case CatCase(value: Cat) +} +``` + +`optional T` fields generate `Option[T]`. Reference tracking uses `@Ref`. + +### Registration + +Generated registration helpers register Scala serializers, enums, structs, and +unions for `Fory` and `ThreadSafeFory`: + +```scala +object AddressbookForyRegistration { + def register(fory: Fory): Unit = { + ScalaSerializers.registerSerializers(fory) + ScalaSerializers.registerEnum(fory, classOf[Person.PhoneType], 101L) + ForySerializer.register(fory, classOf[Person.PhoneNumber], 102L) + ForySerializer.register(fory, classOf[Person], 100L) + ForySerializer.register(fory, classOf[Animal], 106L) + } +} +``` + +Run the end-to-end Scala IDL matrix with: + +```bash +cd integration_tests/idl_tests +./run_scala_tests.sh +``` + +The runner regenerates Scala fixtures, runs Scala 3 IDL tests, and then runs the +Java peer matrix with `IDL_PEER_LANG=scala`. + ## Cross-Language Notes ### Type ID Behavior diff --git a/docs/compiler/index.md b/docs/compiler/index.md index 286de0b5cc..e0317ed22b 100644 --- a/docs/compiler/index.md +++ b/docs/compiler/index.md @@ -21,7 +21,7 @@ license: | Fory IDL is a schema definition language for Apache Fory that enables type-safe cross-language serialization. Define your data structures once and generate -native data structure code for Java, Python, Go, Rust, C++, C#, Swift, JavaScript, and Dart. +native data structure code for Java, Python, Go, Rust, C++, C#, Swift, JavaScript, Dart, and Scala. ## Example Schema @@ -104,6 +104,7 @@ Generated code uses native language constructs: - JavaScript: Interfaces with registration function - Swift: Fory model macros with field/case metadata and registration helpers - Dart: `@ForyStruct` classes with `@ForyField` annotations and registration helpers +- Scala: Scala 3 `case class`, normal class, enum, and ADT enum models with macro-derived serializers ## Quick Start @@ -141,7 +142,7 @@ message Person { foryc example.fdl --output ./generated # Generate for specific languages -foryc example.fdl --lang java,python,csharp,javascript,swift,dart --output ./generated +foryc example.fdl --lang java,python,csharp,javascript,swift,dart,scala --output ./generated ``` ### 4. Use Generated Code @@ -197,11 +198,11 @@ message Example { Fory IDL types map to native types in each language: -| Fory IDL Type | Java | Python | Go | Rust | C++ | C# | JavaScript | Swift | Dart | -| ------------- | --------- | -------------- | -------- | -------- | ------------- | -------- | ---------- | -------- | -------- | -| `int32` | `int` | `pyfory.Int32` | `int32` | `i32` | `int32_t` | `int` | `number` | `Int32` | `int` | -| `string` | `String` | `str` | `string` | `String` | `std::string` | `string` | `string` | `String` | `String` | -| `bool` | `boolean` | `bool` | `bool` | `bool` | `bool` | `bool` | `boolean` | `Bool` | `bool` | +| Fory IDL Type | Java | Python | Go | Rust | C++ | C# | JavaScript | Swift | Dart | Scala | +| ------------- | --------- | -------------- | -------- | -------- | ------------- | -------- | ---------- | -------- | -------- | --------- | +| `int32` | `int` | `pyfory.Int32` | `int32` | `i32` | `int32_t` | `int` | `number` | `Int32` | `int` | `Int` | +| `string` | `String` | `str` | `string` | `String` | `std::string` | `string` | `string` | `String` | `String` | `String` | +| `bool` | `boolean` | `bool` | `bool` | `bool` | `bool` | `bool` | `boolean` | `Bool` | `bool` | `Boolean` | See [Type System](schema-idl.md#type-system) for complete mappings. diff --git a/docs/compiler/schema-idl.md b/docs/compiler/schema-idl.md index b7252ed6a8..ab98a0e07f 100644 --- a/docs/compiler/schema-idl.md +++ b/docs/compiler/schema-idl.md @@ -817,7 +817,8 @@ message Person [id=100] { ### Rules -- Case IDs must be unique within the union +- Case IDs must be positive and unique within the union +- Case ID `0` is reserved for language runtimes that expose an unknown-case carrier - Cases cannot be `optional` or `ref` - Union cases do not support field options - Case types can be primitives, enums, messages, or other named types @@ -845,8 +846,7 @@ field_type field_name = field_number; ```protobuf optional list<string> tags = 1; // Nullable list list<optional string> tags = 2; // Elements may be null -ref list<Node> nodes = 3; // Collection tracked as a reference -list<ref Node> nodes = 4; // Elements tracked as references +list<ref Node> nodes = 3; // Elements tracked as references ``` **Grammar:** @@ -859,8 +859,9 @@ list_type := 'list' '<' { 'optional' | 'ref' | scalar_encoding } field_type ' array_type := 'array' '<' array_element_type '>' ``` -Modifiers apply to the field/collection. Use `list<...>` to describe element -modifiers. `repeated` is accepted as an alias for `list`. +`optional` before `list` applies to the collection field. `ref` is only valid +for named message/union fields; for collection contents, use `list<ref T>` or +`map<K, ref V>`. `repeated` is accepted as an alias for `list`. ### Field Modifiers @@ -886,6 +887,7 @@ message User { | C++ | `std::string name` | `std::optional<std::string> name` | | JavaScript | `name: string` | `name?: string \| null` | | Dart | `String name` | `String? email` | +| Scala | `name: String` | `email: Option[String]` | **Default Values:** @@ -916,13 +918,14 @@ message Node { | Language | Without `ref` | With `ref` | | ---------- | -------------- | ------------------------------------------ | -| Java | `Node parent` | `Node parent` with `@ForyField(ref=true)` | +| Java | `Node parent` | `Node parent` with `@Ref` | | Python | `parent: Node` | `parent: Node = pyfory.field(ref=True)` | | Go | `Parent Node` | `Parent *Node` with `fory:"ref"` | | Rust | `parent: Node` | `parent: Arc<Node>` | | C++ | `Node parent` | `std::shared_ptr<Node> parent` | | JavaScript | `parent: Node` | `parent: Node` (no ref distinction) | | Dart | `Node parent` | `Node parent` with `@ForyField(ref: true)` | +| Scala | `parent: Node` | `parent: Node @Ref` | Rust uses `Arc` by default; use `ref(thread_safe=false)` or `ref(weak=true)` to customize pointer types. For protobuf option syntax, see @@ -959,23 +962,22 @@ Modifiers can be combined: message Example { optional list<string> tags = 1; // Nullable list list<optional string> aliases = 2; // Elements may be null - ref list<Node> nodes = 3; // Collection tracked as a reference - list<ref Node> children = 4; // Elements tracked as references - optional ref User owner = 5; // Nullable tracked reference + list<ref Node> children = 3; // Elements tracked as references + optional ref User owner = 4; // Nullable tracked reference } ``` -Modifiers before `list` apply to the field/collection. Modifiers after `list` -apply to elements. `repeated` is accepted as an alias for `list`. +`optional` before `list` applies to the field/collection. `ref` before `list` or +`map` is invalid; put `ref` inside the element/value type instead. `repeated` is +accepted as an alias for `list`. **List modifier mapping:** -| Fory IDL | Java | Python | Go | Rust | C++ | Dart | -| ----------------------- | --------------------------------------- | --------------------------------------- | ----------------------- | --------------------- | ----------------------------------------- | ------------------------------------------------------------- | -| `optional list<string>` | `@Nullable List<String>` | `Optional[List[str]]` | `[]string` + `nullable` | `Option<Vec<String>>` | `std::optional<std::vector<std::string>>` | `List<String>?` | -| `list<optional string>` | `List<String>` (nullable elements) | `List[Optional[str]]` | `[]*string` | `Vec<Option<String>>` | `std::vector<std::optional<std::string>>` | `List<String?>` | -| `ref list<User>` | `List<User>` + `@ForyField(ref = true)` | `List[User]` + `pyfory.field(ref=True)` | `[]User` + `ref` | `Arc<Vec<User>>` | `std::shared_ptr<std::vector<User>>` | `List<User>` + `@ForyField(ref: true)` | -| `list<ref User>` | `List<User>` | `List[User]` | `[]*User` + `ref=false` | `Vec<Arc<User>>` | `std::vector<std::shared_ptr<User>>` | `List<User>` + `@ListField(element: DeclaredType(ref: true))` | +| Fory IDL | Java | Python | Go | Rust | C++ | Dart | Scala | +| ----------------------- | ---------------------------------- | --------------------- | ----------------------- | --------------------- | ----------------------------------------- | ------------------------------------------------------------- | ---------------------- | +| `optional list<string>` | `@Nullable List<String>` | `Optional[List[str]]` | `[]string` + `nullable` | `Option<Vec<String>>` | `std::optional<std::vector<std::string>>` | `List<String>?` | `Option[List[String]]` | +| `list<optional string>` | `List<String>` (nullable elements) | `List[Optional[str]]` | `[]*string` | `Vec<Option<String>>` | `std::vector<std::optional<std::string>>` | `List<String?>` | `List[Option[String]]` | +| `list<ref User>` | `List<@Ref User>` | `List[User]` | `[]*User` + `ref=false` | `Vec<Arc<User>>` | `std::vector<std::shared_ptr<User>>` | `List<User>` + `@ListField(element: DeclaredType(ref: true))` | `List[User @Ref]` | Use `ref(thread_safe=false)` in Fory IDL (or `[(fory).thread_safe_pointer = false]` in protobuf) to generate `Rc` instead of `Arc` in Rust. @@ -1360,6 +1362,11 @@ code. When `enable_auto_type_id = false`, types without explicit IDs are registered by namespace and name instead. Collisions are detected at compile-time across the current file and all imports; when a collision occurs, the compiler raises an error and asks for an explicit `id` or an `alias`. +For Java and Scala generated code, nested name registration appends the parent +path to the namespace and keeps the nested type's simple name. For example, +`package demo; message Envelope { message Payload { ... } }` registers +`Payload` as namespace `demo.Envelope` and type name `Payload` in those JVM +targets. ```protobuf enum Color [id=100] { ... } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
