Generating Constructors in Scala

I did some exercises in Scala. I thought I would try to deduce a method for creating incompatible value types that cannot be accidentally assigned to each other using the recently added AnyVal .

The best I could come up with was something like this:

 object Measurements { trait ValueType[T] extends Any { def value: T } trait Measurement[A <: ValueType[Double]] extends Any { def modify(fn: (Double, A) => Double, value: A): A def +(mod: A) = modify((x: Double, y: A) => x + y.value, mod) def -(mod: A) = modify((x: Double, y: A) => x - y.value, mod) def *(mod: A) = modify((x: Double, y: A) => x * y.value, mod) def /(mod: A) = modify((x: Double, y: A) => x / y.value, mod) } case class Frequency(value: Double) extends AnyVal with ValueType[Double] with Measurement[Frequency] { def modify(fn: (Double, Frequency) => Double, mod: Frequency) = Frequency(fn(value, mod)) } case class Amplitude(value: Double) extends AnyVal with ValueType[Double] with Measurement[Amplitude] { def modify(fn: (Double, Amplitude) => Double, mod: Amplitude) = Amplitude(fn(value, mod)) } case class Wavelength(value: Double) extends AnyVal with ValueType[Double] with Measurement[Wavelength] { def modify(fn: (Double, Wavelength) => Double, mod: Wavelength) = Wavelength(fn(value, mod)) } } import Measurements._ Frequency(150) + Frequency(10) // ==> Frequency(160) Amplitude(23.2) * Amplitude(2) // ==> Amplitude(46.4) Amplitude(50) + Frequency(50) // ==> Compile-time Type Error 

Unfortunately, for each instance, you need to uniquely define a modify function, because it is impossible to define something like A(value) with a common type A There seems to be no way to define constructor constraints. Otherwise, I could define something in common in the dash, for example:

 def modify(fn: (Double, A) => Double, mod: A) = A(fn(value, mod)) 

I tried calling apply(Double) on A , but it is not available from a shared variable. I also tried to understand if I could somehow create a factory in order to at least simplify things, but could not come up with anything more graceful than what I am doing now. I am facing the same issue with C # all the time.

Is there a way to account for this code, which relies on a generic constructor type for different (but related) classes?

+4
source share
1 answer

I do not think that this is possible without resorting to time reflection (or, possibly, macros). There are basically three problems that you already mentioned two:

  • It is not possible for a sign to declare a mandatory constructor signature.

  • It is not possible to call methods such as apply on A because it is a type variable, not a class or object.

  • Because value classes can (currently) extend universal features , but not abstract classes, it is not possible to use TypeTags to get a class that is of the type that A creates when the Measurement extension extends.

The best I could come up with is the following approach. Note that it uses reflection and that exceptions can be thrown at runtime if a specific instance of Measurement does not declare the appropriate constructor.

 // ... as above ... trait Measurement[A <: ValueType[Double]] extends Any { self: A => def modify(fn: (Double, A) => Double, mod: A): A = this.getClass .getConstructor(this.getClass) .newInstance(fn(value, mod): java.lang.Double) // ... as above ... } case class Frequency(value: Double) extends AnyVal with ValueType[Double] with Measurement[Frequency] // ... etc ... Frequency(150) + Frequency(10) // ==> Frequency(160) Amplitude(23.2) * Amplitude(2) // ==> Amplitude(46.4) Amplitude(50) + Frequency(50) // ==> Compile-time Type Error 

The self: A -test annotation ensures that the value field declared by ValueType is accessible from the internal attribute Measurement .

+4
source

All Articles