Can a scala class instance spawn a variable not defined in its constructor?

Solution for Can a scala class instance spawn a variable not defined in its constructor?
is Given Below:

I wonder if I can “patch” a variable onto an instance of a scala class.

The particular application is to fit statistical models à la scikit-learn (python):

from sklearn.linear_model import LogisticRegression 
lr = LogisticRegression() 

hasattr(lr, "coef_") # False

lr.fit([[2,3],[3,3]], [0,1])
hasattr(lr, "coef_") # True

sklearn checks if coef_ is present to define the “fitted” state of a model object (check_is_fitted essentially looks for attributes ending in _).

What I have tried:

  • Forbidding unfitted states – I have worked around the problem by not allowing users to instantiate unfitted models, i.e. the class constructor requires input data to be fitted
    • This is ok ATM so I am mostly being curious here
    • That said, having unfitted states could later be handy to enable model pipelines (e.g. here)
  • Using mutable variables initialised with random numbers (see dummy example below)
    • That’s ok if I know the type and shape of the coefficients, though instantiating them with random values can be difficult in some cases (depends on the model)
    • I hit a roadblock when writing interfaces around external models; for example if my fit method calls this logistic regression implementation, how am I supposed to access/initialise the underlying coeffients?
class StatsModel{
     var coef: Vector[Int] = Vector(0,0)
     var isFitted: Boolean = false
     
     def fit = {
         coef = Vector(1,2)
         isFitted = true
    } 
} 
@ val m = new StatsModel() 
m: StatsModel = ammonite.$sess.cmd44$StatsModel@62cf8fda

@ m.isFitted 
res46: Boolean = false

@ m.fit 


@ m.isFitted 
res48: Boolean = true

In general, you can’t do that in Scala (all sorts of black magic is possible in the JVM via reflection (I’m not saying there’s a way it could be accomplished via reflection, mind), but that’s not really Scala per se).

A purer Scala approach would be along the lines of modeling

  • a regression with coefficients which has not been fit
  • a regression with coefficients which has been fit

as different types

e.g.

sealed trait StatsModel {
  def coefficients: Seq[Int]

  def fit(a: Seq[Seq[Int]], b: Seq[Int]): StatsModel

  /* Might not actually need this, but if there's a chance of multiple kinds of fitted or unfitted models, this might come in handy.
   * We'll use the JVM's v-table to resolve this, though if there are a lot of instances, defining a pattern match here might be more performant.
   */
  def isFit: Boolean
}

object StatsModel {
  case object Unfitted extends StatsModel {
    def coefficients: Seq[Int] = Nil
    def isFit: Boolean = false

    def fit(a: Seq[Seq[Int]], b: Seq[Int]): Fitted = ???
  }

  case class Fitted(override val coefficients: Vector[Int]) extends StatsModel {
    def isFit: Boolean = false

    def fit(a: Seq[Seq[Int]], b: Seq[Int]): StatsModel = this  // no need to perform the fit again...
  }
}