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 907c5fea7f368527c791dae36dc5084f8aa8c1af
Author: chaokunyang <[email protected]>
AuthorDate: Fri May 15 11:18:15 2026 +0000

    πŸ”„ synced local 'docs/guide/' with remote 'docs/guide/'
---
 docs/guide/java/field-configuration.md            |  40 +++--
 docs/guide/kotlin/static-generated-serializers.md |   4 +-
 docs/guide/scala/fory-creation.md                 |  20 +++
 docs/guide/scala/index.md                         |   4 +-
 docs/guide/scala/schema-idl.md                    | 179 ++++++++++++++++++++++
 docs/guide/xlang/field-nullability.md             |   1 +
 docs/guide/xlang/field-reference-tracking.md      |  39 ++++-
 7 files changed, 263 insertions(+), 24 deletions(-)

diff --git a/docs/guide/java/field-configuration.md 
b/docs/guide/java/field-configuration.md
index 1d119c1fc0..d625eeccad 100644
--- a/docs/guide/java/field-configuration.md
+++ b/docs/guide/java/field-configuration.md
@@ -25,8 +25,9 @@ This page explains how to configure field-level metadata for 
serialization in Ja
 
 Apache Foryβ„’ provides field-level configuration through annotations:
 
-- **`@ForyField`**: Configure field metadata (id, ref, dynamic)
+- **`@ForyField`**: Configure field metadata (id, dynamic)
 - **`@Nullable`**: Mark a field type or nested type position as nullable
+- **`@Ref`**: Enable field or nested-element reference tracking
 - **`@Ignore`**: Exclude fields from serialization
 - **Integer type annotations**: Control integer encoding (varint, fixed, 
tagged, unsigned)
 
@@ -76,8 +77,8 @@ public class User {
     @ForyField(id = 2)
     private String email;
 
-    @ForyField(id = 3, ref = true)
-    private List<User> friends;
+    @ForyField(id = 3)
+    private List<@Ref User> friends;
 
     @ForyField(id = 4, dynamic = ForyField.Dynamic.TRUE)
     private Object data;
@@ -89,11 +90,11 @@ public class User {
 | Parameter | Type      | Default | Description                            |
 | --------- | --------- | ------- | -------------------------------------- |
 | `id`      | `int`     | `-1`    | Non-negative field tag ID, or no ID    |
-| `ref`     | `boolean` | `false` | Enable reference tracking              |
 | `dynamic` | `Dynamic` | `AUTO`  | Control polymorphism for struct fields |
 
 Use `@Nullable` on the field type or nested type position for nullable schema
-metadata. `@ForyField` does not carry nullability.
+metadata and `@Ref` for reference tracking. `@ForyField` does not carry either
+setting.
 
 ## Field ID (`id`)
 
@@ -164,7 +165,7 @@ public class Record {
 - When a field is non-nullable, Fory skips writing the null flag.
 - Boxed types (`Integer`, `Long`, etc.) that can be null should use 
`@Nullable`.
 
-## Reference Tracking (`ref`)
+## Reference Tracking (`@Ref`)
 
 Enable reference tracking for fields that may be shared or circular:
 
@@ -172,11 +173,13 @@ Enable reference tracking for fields that may be shared 
or circular:
 public class RefOuter {
     // Both fields may point to the same inner object
     @Nullable
-    @ForyField(id = 0, ref = true)
+    @ForyField(id = 0)
+    @Ref
     private RefInner inner1;
 
     @Nullable
-    @ForyField(id = 1, ref = true)
+    @ForyField(id = 1)
+    @Ref
     private RefInner inner2;
 }
 
@@ -186,7 +189,8 @@ public class CircularRef {
 
     // Self-referencing field for circular references
     @Nullable
-    @ForyField(id = 1, ref = true)
+    @ForyField(id = 1)
+    @Ref
     private CircularRef selfRef;
 }
 ```
@@ -198,8 +202,8 @@ public class CircularRef {
 
 **Notes**:
 
-- Default is `ref = false` (no reference tracking)
-- When `ref = false`, avoids IdentityMap overhead and skips ref tracking flag
+- Fields without `@Ref` do not use field-wrapper reference tracking
+- Avoid `@Ref` when values are not shared or circular, so Fory can skip the 
reference flag
 - Reference tracking only takes effect when global ref tracking is enabled
 
 ## Dynamic (Polymorphism Control)
@@ -451,7 +455,8 @@ public class Document {
 
     // Reference-tracked field for shared/circular references
     @Nullable
-    @ForyField(id = 9, ref = true)
+    @ForyField(id = 9)
+    @Ref
     private Document parent;
 
     // Ignored field (not serialized)
@@ -578,13 +583,13 @@ public class User {
 Xlang mode has **stricter default values** due to type system differences 
between languages:
 
 - **Nullable**: Fields are non-nullable by default
-- **Ref tracking**: Disabled by default (`ref = false`)
+- **Ref tracking**: Disabled by default unless the field type uses `@Ref`
 - **Polymorphism**: Concrete types are non-polymorphic by default
 
 In xlang mode, you **need to configure fields** when:
 
 - A field can be null (use `@Nullable`)
-- A field needs reference tracking for shared/circular objects (use `ref = 
true`)
+- A field needs reference tracking for shared/circular objects (use `@Ref`)
 - Integer types need specific encoding for cross-language compatibility
 - You want to reduce metadata size (use field IDs)
 
@@ -602,7 +607,8 @@ public class User {
     private String email;
 
     @Nullable
-    @ForyField(id = 3, ref = true) // Must declare ref for shared objects
+    @ForyField(id = 3)
+    @Ref // Must declare @Ref for shared objects
     private User friend;
 }
 ```
@@ -619,7 +625,7 @@ public class User {
 
 1. **Configure field IDs**: Recommended for compatible mode to reduce 
serialization cost
 2. **Use `@Nullable` for nullable fields**: Required for fields that can be 
null
-3. **Enable ref tracking for shared objects**: Use `ref = true` when objects 
are shared or circular
+3. **Enable ref tracking for shared objects**: Use `@Ref` when objects are 
shared or circular
 4. **Use `@Ignore` or `transient` for sensitive data**: Passwords, tokens, 
internal state
 5. **Choose appropriate encoding**: `varint` for small values, `fixed` for 
full-range values
 6. **Keep IDs stable**: Once assigned, don't change field IDs
@@ -631,7 +637,7 @@ public class User {
 | ----------------------------- | -------------------------------------- |
 | `@ForyField(id = N)`          | Field tag ID to reduce metadata size   |
 | `@Nullable`                   | Mark field or nested type as nullable  |
-| `@ForyField(ref = true)`      | Enable reference tracking              |
+| `@Ref`                        | Enable reference tracking              |
 | `@ForyField(dynamic = ...)`   | Control polymorphism for struct fields |
 | `@Ignore`                     | Exclude field from serialization       |
 | `@Int32Type(encoding = ...)`  | 32-bit signed integer encoding         |
diff --git a/docs/guide/kotlin/static-generated-serializers.md 
b/docs/guide/kotlin/static-generated-serializers.md
index 8836ac44e1..99eb4edb38 100644
--- a/docs/guide/kotlin/static-generated-serializers.md
+++ b/docs/guide/kotlin/static-generated-serializers.md
@@ -131,8 +131,8 @@ the schema is always read from Kotlin source nullability.
 
 ## References
 
-`@ForyField(ref = true)` is not supported by Kotlin xlang generated
-serializers. Generated reads construct Kotlin values through primary
+Kotlin xlang generated serializers reject every `@Ref` annotation, including
+`@Ref(enable = false)`. Generated reads construct Kotlin values through primary
 constructors, so they cannot publish partially constructed objects for cyclic
 back-references. Use non-cyclic schemas for Kotlin xlang structs.
 
diff --git a/docs/guide/scala/fory-creation.md 
b/docs/guide/scala/fory-creation.md
index b79e618563..a3f0a2ba6e 100644
--- a/docs/guide/scala/fory-creation.md
+++ b/docs/guide/scala/fory-creation.md
@@ -133,3 +133,23 @@ val fory = Fory.builder()
 
 ScalaSerializers.registerSerializers(fory)
 ```
+
+## Cross-Language Mode
+
+For Scala xlang or schema IDL generated code, enable xlang and register the
+Scala serializers before registering generated model types:
+
+```scala
+val fory = Fory.builder()
+  .withXlang(true)
+  .withCompatible(true)
+  .withScalaOptimizationEnabled(true)
+  .build()
+
+ScalaSerializers.registerSerializers(fory)
+ExampleForyRegistration.register(fory)
+```
+
+In xlang mode, Scala collections use canonical `list`, `set`, and `map`
+payloads instead of Scala factory payloads. Generated optional fields use
+`Option[T]`.
diff --git a/docs/guide/scala/index.md b/docs/guide/scala/index.md
index 60d5da14c5..382b43e1c1 100644
--- a/docs/guide/scala/index.md
+++ b/docs/guide/scala/index.md
@@ -29,7 +29,8 @@ Apache Foryβ„’ Scala provides optimized serializers for Scala 
types, built on to
 - `Option` types
 - Scala 2 and 3 enumerations
 
-Both Scala 2 and Scala 3 are supported.
+The runtime artifact supports Scala 2.13 and Scala 3. Schema IDL generated
+Scala source and macro-derived xlang serializers require Scala 3.
 
 ## Features
 
@@ -98,3 +99,4 @@ Fory Scala is built on top of Fory Java. Most configuration 
options, features, a
 - [Fory Creation](fory-creation.md) - Scala-specific Fory setup requirements
 - [Type Serialization](type-serialization.md) - Serializing Scala types
 - [Default Values](default-values.md) - Scala class default values support
+- [Schema IDL And Xlang](schema-idl.md) - Scala 3 generated models and 
macro-derived xlang serializers
diff --git a/docs/guide/scala/schema-idl.md b/docs/guide/scala/schema-idl.md
new file mode 100644
index 0000000000..7f7dd6cf33
--- /dev/null
+++ b/docs/guide/scala/schema-idl.md
@@ -0,0 +1,179 @@
+---
+title: Schema IDL And Xlang
+sidebar_position: 4
+id: schema_idl
+license: |
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+---
+
+The Fory schema IDL Scala target generates Scala 3 source for xlang payloads.
+The runtime artifact remains cross-built for Scala 2.13 and Scala 3; only the
+schema IDL output and quoted macro derivation require Scala 3.
+
+## Setup
+
+Generated Scala code uses the public macro API in `org.apache.fory.scala` and
+the shared JVM annotations in `org.apache.fory.annotation`. Macro internals 
live
+under `org.apache.fory.scala.internal`.
+
+```scala
+import org.apache.fory.Fory
+import org.apache.fory.scala.ForySerializer
+import org.apache.fory.serializer.scala.ScalaSerializers
+
+val fory = Fory.builder()
+  .withXlang(true)
+  .withCompatible(true)
+  .withScalaOptimizationEnabled(true)
+  .build()
+
+ScalaSerializers.registerSerializers(fory)
+ExampleForyRegistration.register(fory)
+```
+
+For `ThreadSafeFory`, generated registration helpers install a callback so each
+runtime instance gets the same serializers.
+
+Generated helpers register message type identities before installing message
+serializers. This two-phase order lets mutually recursive message graphs build
+descriptor metadata through the normal `TypeResolver` path without placeholder
+serializers or Scala-specific registration state in Java core. Enums and unions
+are registered with their serializers directly because their derived 
serializers
+own case dispatch.
+
+## Generated Messages
+
+Acyclic messages 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 = 2) email: Option[String]
+) derives ForySerializer
+```
+
+Schema `optional T` fields are stored as `Option[T]`.
+
+Messages in compiler-detected construction cycles generate normal classes with
+mutable serialized fields so the deserializer can allocate and register the
+object before reading fields that can point back to it. A top-level `ref Foo`,
+nested `list<ref Foo>`, or `any` field does not by itself force this shape.
+The compiler analyzes message and union dependencies together, so
+message-to-union-to-message cycles also make the participating messages normal
+classes. Acyclic owner messages that only contain a cyclic nested type remain
+case classes.
+
+Reference tracking is expressed with the shared `@Ref` annotation, including
+type-use positions:
+
+```scala
+@ForyStruct
+final class Node() derives ForySerializer {
+  @ForyField(id = 1)
+  var children: List[Node @Ref] = List.empty
+
+  @Ref
+  @ForyField(id = 2)
+  var parent: Option[Node @Ref] = None
+}
+```
+
+`@Ref` is the JVM reference-tracking annotation for Scala macro and IDL APIs.
+
+Generated xlang collection fields use immutable Scala collection types:
+`List[T]`, `Set[T]`, and `Map[K, V]`. The runtime xlang serializers can also
+rebuild supported mutable collection interfaces such as `scala.collection.Seq`
+and `scala.collection.Map`, but concrete mutable collection classes are outside
+the schema IDL surface unless explicitly generated.
+
+## Generated Enums
+
+IDL enums generate Scala 3 enums only. No Java enum sidecar is emitted.
+
+```scala
+import org.apache.fory.annotation.ForyEnumId
+
+enum Status {
+  @ForyEnumId(0)
+  case Unknown
+
+  @ForyEnumId(1)
+  case Ok
+}
+```
+
+Generated registration uses `ScalaSerializers.registerEnum(...)` so the stable
+Fory enum IDs from case-level `@ForyEnumId` metadata are used in xlang mode.
+
+## Generated Unions
+
+IDL unions generate Scala 3 ADT enums with macro-derived serializers:
+
+```scala
+import org.apache.fory.annotation.{ForyCase, ForyUnion, UInt32Type}
+import org.apache.fory.config.Int32Encoding
+import org.apache.fory.scala.ForySerializer
+
+@ForyUnion
+enum SearchTarget derives ForySerializer {
+  @ForyCase(id = 0)
+  case UnknownCase(caseId: Int, value: Any)
+
+  @ForyCase(id = 1)
+  case UserCase(value: User)
+
+  @ForyCase(id = 2)
+  case FixedIdCase(value: Long @UInt32Type(encoding = Int32Encoding.FIXED))
+}
+```
+
+Schema-defined union cases must use positive IDs. Case ID `0` is reserved for
+the Scala unknown-case carrier, whose payload stores the original positive case
+ID and the deserialized value. When a reader sees a newer positive case ID, it
+returns `UnknownCase(originalId, value)` instead of failing solely because the
+case ID is not known locally.
+
+The macro writes the existing xlang union envelope directly. It does not
+allocate temporary Java `Union` carriers.
+
+## Manual Scala 3 Derivation
+
+Manual Scala 3 models can derive the same serializer typeclass:
+
+```scala
+@ForyStruct
+final class Record(@ForyField(id = 1) val id: Int) derives ForySerializer {
+  @ForyField(id = 2)
+  var name: String = ""
+}
+```
+
+The macro generates direct constructor calls for constructor-owned fields and
+direct assignments for mutable post-construction fields. It builds descriptor
+metadata from Scala compile-time types, including nested generics, `Option`,
+arrays, scalar encoding annotations, nullability, and `@Ref` metadata. Java
+reflection is not the source of truth for generated Scala metadata.
+
+During copy, cyclic graphs are supported when the copied root can be allocated
+and registered before cyclic fields are copied, which is the normal-class shape
+used by schema IDL for construction cycles. If a copy starts at an immutable
+constructor-owned value that participates in the cycle, such as a Scala enum
+case or case class, the serializer fails with a clear error because no copied
+identity can be published until construction has completed.
diff --git a/docs/guide/xlang/field-nullability.md 
b/docs/guide/xlang/field-nullability.md
index bea7f3f092..5d891fc7d4 100644
--- a/docs/guide/xlang/field-nullability.md
+++ b/docs/guide/xlang/field-nullability.md
@@ -36,6 +36,7 @@ The following types are nullable by default:
 - Go pointer types (`*int32`, `*string`, etc.)
 - Rust `Option<T>`
 - Python `Optional[T]`
+- Scala `Option[T]`
 
 | Field Type                                 | Default Nullable | Null Flag 
Written |
 | ------------------------------------------ | ---------------- | 
----------------- |
diff --git a/docs/guide/xlang/field-reference-tracking.md 
b/docs/guide/xlang/field-reference-tracking.md
index 9d32c0e88b..a580cd2070 100644
--- a/docs/guide/xlang/field-reference-tracking.md
+++ b/docs/guide/xlang/field-reference-tracking.md
@@ -69,6 +69,17 @@ let fory = Fory::builder()
     .track_ref(true).build();
 ```
 
+### Scala
+
+```scala
+val fory = Fory.builder()
+  .withXlang(true)
+  .withCompatible(true)
+  .withRefTracking(true)
+  .withScalaOptimizationEnabled(true)
+  .build()
+```
+
 ## Wire Format
 
 When reference tracking is enabled, nullable fields write a **ref flag byte** 
before the value:
@@ -121,10 +132,11 @@ By default, **most fields do not track references** even 
when global `refTrackin
 | Go       | No                   | None (use `fory:"ref"` to enable)          
                |
 | C++      | Yes                  | `std::shared_ptr<T>`, 
`fory::serialization::SharedWeak<T>` |
 | Rust     | No                   | `Rc<T>`, `Arc<T>`, `Weak<T>`               
                |
+| Scala    | No                   | None (use `@Ref` to enable)                
                |
 
 ### Customizing Per-Field Ref Tracking
 
-#### Java: @ForyField Annotation
+#### Java: @Ref Annotation
 
 ```java
 public class Document {
@@ -132,12 +144,11 @@ public class Document {
     String title;
 
     // Enable ref tracking for this field
-    @ForyField(ref = true)
+    @Ref
     Author author;
 
     // Shared across documents, track refs to avoid duplicates
-    @ForyField(ref = true)
-    List<Tag> tags;
+    List<@Ref Tag> tags;
 }
 ```
 
@@ -183,6 +194,26 @@ struct Document {
 }
 ```
 
+#### Scala: @Ref Annotation
+
+Scala schema IDL and Scala 3 macro derivation use the same shared JVM `@Ref`
+annotation:
+
+```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 children: List[Node @Ref] = List.empty
+
+  @Ref
+  @ForyField(id = 2)
+  var parent: Option[Node @Ref] = None
+}
+```
+
 #### Go: Struct Tags
 
 ```go


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to