0

I've created (I think) an enumeration which has an associated type, under the constraint that the associated type has to be an implementation of the Numeric trait:

  sealed trait DataType[T <: Numeric[T]] { type NumT = T }
  trait Positions extends DataType[Double] 
  trait Values extends DataType[Double]
  trait Weights extends DataType[Double]
  trait Prices extends DataType[Double]

This seems all well and maybe even good, but I'd then like to use this enumeration in a case class, without having to duplicate the type parameter - so code in the spirit of this non-working example directly below:

  case class SingleValue[DataT <: DataType] (
    datum: DataT#NumT,
  ) {
    def addToDatum(plus: DataT#NumT): SingleValue[DataT] = (this.datum += plus)
  }

This doesn't compile, and I can't make it work - the compiler asks for a type parameter on DataType, but even if given one - like

  case class SingleValue[T <: Numeric[T], DataT <: DataType[T]]

I am still hitting problems, and in this case I am repeating myself and the advantage of having an enumeration is mostly lost anyway.

Could anybody steer me on the right course?

Thanks!

Chris J Harris
  • 1,049
  • 7
  • 14
  • There is no enumeration in this code so it's not clear what you mean by that. – jwvh Aug 26 '20 at 09:57
  • @jwvh My understanding is that using a sealed trait with multiple extensions was the idiomatic way to do enumerations in Scala. – Chris J Harris Aug 26 '20 at 10:40
  • 1
    What you've got is an [Algebraic Data Type](https://stackoverflow.com/q/5911267/4993128). More specifically it's a sum type ADT. Idiomatic enumeration starts by extending the [Enumeration class](https://www.scala-lang.org/files/archive/api/current/scala/Enumeration.html). – jwvh Aug 26 '20 at 10:58

1 Answers1

2

Just remove the type parameter from DataType.

sealed trait DataType {
  type NumT <: Numeric[NumT]
}
trait Positions extends DataType {
  type NumT = Double
}

But note that this won't compile, for the same reason that your original code doesn't compile: Double is not a subtype of Numeric[Double].

You could use a different definition of DataType though:

sealed trait DataType {
  type NumT
  implicit def numeric: Numeric[NumT]
}
Matthias Berndt
  • 2,847
  • 1
  • 8
  • 20
  • thanks for this. I maybe being dense, but if I use your second code block for the DataType trait, I get an error: `value += is not a member of DataT#NumT Expression does not convert to assignement because receiver not assignable`. Do I also need to alter the case class itself? – Chris J Harris Aug 26 '20 at 10:40
  • 2
    += doesn't work because datum is declared without the var keyword and is hence immutable. Also you need to somehow access the numeric instance, which is currently impossible because you don't have access to any DataType instance in SingleValue. But I think if we're going to have a useful discussion, we need to talk more about what you're actually trying to achieve here rather than the mechanics. – Matthias Berndt Aug 26 '20 at 12:07
  • I am basically just trying to specify a (numeric) type for this case class when it is instantiated. I'm using an enumeration (or not, see comments above from @jwvh) because I have a list of potential types that the user of the API should choose from, defined at the start. Perhaps an Enumeration is the easiest way here. I may have got carried away with this odd system. – Chris J Harris Aug 26 '20 at 12:35