Scala
The Scala target emits Scala 3 source only. The fory-scala 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 - Schema module:
AddressbookForyModule.scala
For schemas without a Scala package, the schema module name is derived from the
source file stem, for example main.fdl generates MainForyModule.scala.
Scala import graphs cannot mix default-package schemas with named Scala
packages.
Type Generation
Messages outside compiler-detected construction cycles generate case classes:
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 {
def toBytes(): Array[Byte] =
AddressbookForyModule.getFory.serialize(this)
}
object Person {
def fromBytes(bytes: Array[Byte]): Person =
AddressbookForyModule.getFory.deserialize(bytes).asInstanceOf[Person]
}
Messages in circular construction cycles generate normal classes with mutable serialized fields so reads can register the object before reading back-references:
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] = None
}
Enums generate Scala 3 enums with stable Fory IDs:
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. Unknown(UnknownCase) is the Fory-provided
forward-compatibility carrier marked with @ForyUnknownCase. It is omitted
from the schema case table because the marker only selects the carrier and does
not add a schema entry. Schema-defined cases use non-negative @ForyCase IDs.
A typed union must have at least one
non-Unknown case.
package addressbook
import org.apache.fory.annotation.{ForyCase, ForyUnion, ForyUnknownCase}
import org.apache.fory.scala.ForySerializer
import org.apache.fory.`type`.union.UnknownCase
@ForyUnion
enum Animal derives ForySerializer {
@ForyUnknownCase
case Unknown(value: UnknownCase)
@ForyCase(id = 0)
case Dog(value: _root_.addressbook.Dog)
@ForyCase(id = 1)
case Cat(value: _root_.addressbook.Cat)
}
Packaged Scala output keeps the schema case name and qualifies the payload type
when both have the same simple name. If a target output mode cannot express a
legal qualifier for a conflict, the compiler appends Case to the generated
case name.
optional T fields generate Option[T]. Top-level message references use
@Ref on the field or constructor parameter. Nested element/value references
use type-use annotations such as List[Node @Ref].
Schema Module
Generated schema modules register schema serializers, enums, structs, and
unions. The package-owned helper Fory instance uses
ForyScala.builder().withXlang(true) with the schema module installed, so
message toBytes/fromBytes helpers work without caller-managed Fory setup:
object AddressbookForyModule extends org.apache.fory.ForyModule {
private lazy val fory: ThreadSafeFory =
ForyScala.builder()
.withXlang(true)
.withRefTracking(true)
.withModule(this)
.buildThreadSafeFory()
private[addressbook] def getFory: ThreadSafeFory = fory
override def install(fory: Fory): Unit = {
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)
}
}
gRPC Service Companions
With --grpc, Scala emits one <ServiceName>Grpc.scala object per local service in the generated models' package. It exposes SERVICE_NAME, service and method descriptors, <ServiceName>ImplBase, and <ServiceName>Client. See Scala gRPC for RpcFuture, RpcIterator, grpc-java variants, and lifecycle guidance.