Scala macro annotations for generating parameter getter and setter methods in CatBoost Spark ML components
npx @tessl/cli install tessl/maven-ai-catboost--catboost-spark-macros_2-13@1.2.0CatBoost Spark Macros provides compile-time macro annotations for automatically generating parameter getter and setter methods in Apache Spark ML components. This library eliminates boilerplate code by transforming parameter field declarations into complete getter/setter method pairs at compile time.
pom.xml:
<dependency>
<groupId>ai.catboost</groupId>
<artifactId>catboost-spark-macros_2.13</artifactId>
<version>1.2.8</version>
</dependency>import ai.catboost.spark.params.macros.ParamGetterSetterimport ai.catboost.spark.params.macros.ParamGetterSetter
import org.apache.spark.ml.param._
class MySparkMLComponent extends Params {
// Annotate parameter declarations to auto-generate getters/setters
@ParamGetterSetter
final val learningRate: DoubleParam = new DoubleParam(this, "learningRate", "Learning rate for training")
@ParamGetterSetter
final val maxDepth: IntParam = new IntParam(this, "maxDepth", "Maximum tree depth")
@ParamGetterSetter
final val features: StringArrayParam = new StringArrayParam(this, "features", "Feature column names")
}
// The macro automatically generates these methods:
// def getLearningRate: Double = $(learningRate)
// def setLearningRate(value: Double): this.type = set(learningRate, value)
// def getMaxDepth: Int = $(maxDepth)
// def setMaxDepth(value: Int): this.type = set(maxDepth, value)
// def getFeatures: Array[String] = $(features)
// def setFeatures(value: Array[String]): this.type = set(features, value)The @ParamGetterSetter annotation automatically transforms parameter field declarations into getter and setter method pairs.
@compileTimeOnly("enable macro paradise to expand macro annotations")
private[spark] class ParamGetterSetter extends StaticAnnotationSupported Parameter Types:
// Basic parameter types
BooleanParam → Boolean
DoubleParam → Double
FloatParam → Float
IntParam → Int
LongParam → Long
DurationParam → java.time.Duration
// Array parameter types
DoubleArrayParam → Array[Double]
IntArrayParam → Array[Int]
StringArrayParam → Array[String]
// Generic parameter types
Param[T] → T
EnumParam[T] → T
OrderedStringMapParam[T] → java.util.LinkedHashMap[String, T]Usage Pattern:
The annotation must be applied to parameter field declarations following this exact pattern:
@ParamGetterSetter
final val parameterName: ParameterType = new ParameterType(this, "parameterName", "description")Generated Methods:
For each annotated parameter, the macro generates:
// Getter method - returns current parameter value
def get{CapitalizedParameterName}: ParameterValueType = $(parameterName)
// Setter method - sets parameter value and returns this for chaining
def set{CapitalizedParameterName}(value: ParameterValueType): this.type = set(parameterName, value)The macro transformation logic that processes annotated parameter declarations.
private[spark] object ParamGetterSetterMacro {
/**
* Core macro implementation method that transforms parameter declarations
* @param c Macro context for compilation
* @param annottees Variable arguments representing annotated expressions
* @return Transformed code expression with generated getter/setter methods
*/
def impl(c: whitebox.Context)(annottees: c.Expr[Any]*): c.Expr[Any]
}import ai.catboost.spark.params.macros.ParamGetterSetter
import org.apache.spark.ml.param._
class CatBoostClassifier extends Params {
@ParamGetterSetter
final val iterations: IntParam = new IntParam(this, "iterations", "Number of boosting iterations")
@ParamGetterSetter
final val learningRate: DoubleParam = new DoubleParam(this, "learningRate", "Learning rate")
@ParamGetterSetter
final val catFeatures: IntArrayParam = new IntArrayParam(this, "catFeatures", "Categorical feature indices")
// Generated methods are now available:
// getIterations: Int, setIterations(value: Int): this.type
// getLearningRate: Double, setLearningRate(value: Double): this.type
// getCatFeatures: Array[Int], setCatFeatures(value: Array[Int]): this.type
}val classifier = new CatBoostClassifier()
.setIterations(1000)
.setLearningRate(0.1)
.setCatFeatures(Array(0, 2, 4))
// Access parameter values
val currentIterations = classifier.getIterations // 1000
val currentRate = classifier.getLearningRate // 0.1import ai.catboost.spark.params.macros.ParamGetterSetter
import org.apache.spark.ml.param._
class AdvancedModel extends Params {
@ParamGetterSetter
final val hyperParams: OrderedStringMapParam[Double] =
new OrderedStringMapParam(this, "hyperParams", "Hyperparameter configuration")
@ParamGetterSetter
final val enabled: BooleanParam = new BooleanParam(this, "enabled", "Enable advanced features")
// Generated methods:
// getHyperParams: java.util.LinkedHashMap[String, Double]
// setHyperParams(value: java.util.LinkedHashMap[String, Double]): this.type
// getEnabled: Boolean
// setEnabled(value: Boolean): this.type
}The macro performs compile-time validation and will abort compilation with descriptive error messages for:
Example compile-time errors:
// Error: "Bad paramType: CustomParam"
@ParamGetterSetter
final val custom: CustomParam = new CustomParam(this, "custom", "desc")
// Error: "Param must have one type parameter"
@ParamGetterSetter
final val multi: Param[String, Int] = new Param(this, "multi", "desc")
// Error: "Annotation @ParamGetterSetter can be used only with Param declarations"
@ParamGetterSetter
val notAParam: String = "invalid"The macro requires the following Scala dependencies:
scala.annotation.{StaticAnnotation, compileTimeOnly}scala.language.experimental.macrosscala.reflect.macros.whitebox@compileTimeOnly to ensure the annotation is removed from runtime bytecode