Covariant data structure with numeric types - scala

How do I create a covariant data structure containing numeric data types?
In scala I can create a covariant data structure, with a hierarchy of type parameters:
abstract class Car[+T] {
def value: T
}
class RaceCar extends Car[RacingWheel] {
def value: RacingWheel = new RacingWheel
}
class NormalCar extends Car[BoringWheel] {
def value: BoringWheel = new BoringWheel
}
class Wheel
case class RacingWheel() extends Wheel
case class BoringWheel() extends Wheel
object Car {
def main(args: Array[String]): Unit = {
val rCar: Car[Wheel] = new RaceCar
val nCar: Car[Wheel] = new NormalCar
val listCars: List[Car[Wheel]] = List(rCar, nCar)
}
}
However, when I replace the Wheel's with numeric data types I run into a problem, because the numeric data types do not have a common type parent other than AnyVal:
abstract class Item[+N] {
def value: N
}
class IntItem(x : Int) extends Item[Int] {
override def value: Int = x
}
class DoubleItem(x : Double) extends Item[Double] {
override def value: Double = x
}
object Item {
def main(args: Array[String]): Unit = {
// ERROR Expression of IntItem does not conform to expected type Item[Number]
val iItem: Item[Number] = new IntItem(10)
// ERROR Expression of DoubleItem does not conform to expected type Item[Number]
val dItem: Item[Number] = new DoubleItem(10.9)
val l: List[Item[Number]] = List(iItem, dItem)
// ERROR: Expression of IntItem does not conform to expected type Item[Double]
val iItem2: Item[Double] = new IntItem(10)
val dItem2: Item[Double] = new DoubleItem(10.9)
val l2: List[Item[Double]] = List(iItem2, dItem2)
}
}
Outside of the covariant data structure I get mixed results:
object NumberMain {
val a : Int = 5
val b : Double = 10.0
val list : List[Number] = List(a, b)
// works, so Number must be related to Int and Double, right?
val x : Number = 5
// ERROR: Expression of type Number doesn't conform to expected type Int
val y : Int = x
// does not work, so Number is not related to Int and Double after all...
}
What changes do I have to make to the Item data structure? Can I tell it that all items can be seen as of type numeric?

In Scala, Int and Double do not share hierarchy with a common Number class. The Typeclass pattern can help you in these cases to define extra features for types without a common supertype.
trait MyNumber[A]
implicit case object IntMyNumber extends MyNumber[Int]
implicit case object DoubleMyNumber extends MyNumber[Double]
class Item[N](x: N)(implicit n: MyNumber[N]) {
def value: N = x
}
The Item class constructor now expects a second group of parameters labeled with the implicit keyword. If you call the method without this group of parameters, the compiler tries to find it (an instance of MyNumber) from variables or imports also labeled with implicit (Where does Scala look for implicits?).
We're just defining here that Int and Double implements MyNumber, but it doesn't do anything. We can write then the class as:
class Item[N : MyNumber](x: N) {
def value: N = x
}
And it works as we expected:
val iItem = new Item(1)
val dItem = new Item(1.0)
scala> iItem.value
res1: Int = 1
scala> dItem.value
res2: Double = 1.0
If we try to build an item of anything without an instance of MyNumber, the compiler throws an error:
val sItem = new Item("S")
error: could not find implicit value for evidence parameter of type MyNumber[String]
If you want MyNumber to be any numeric, Scala implements a Numeric typeclass.
Edit
If you want to add some features to the typeclass that are different for each number, you can do:
trait MyNumber[A] {
def doStuff(a: A): Unit
}
implicit case object IntMyNumber extends MyNumber[Int] {
def doStuff(number: Int): Unit = println("Int")
}
implicit case object DoubleMyNumber extends MyNumber[Double] {
def doStuff(number: Double): Unit = println("Double")
}
case class Item[+N](x: N)(implicit myNumber: MyNumber[N]) {
def value: N = x
def doStuff = myNumber.doStuff(x)
}

Related

Dotty cannot infer result type of generic Scala function taking type parameter trait with abstract type

A simple value hierarchy
Imagine this simple trait Value where every implementing class has a value of some type T.
trait Value {
type T
def value: T
}
We have two different implementing classes representing Int and String values respectively.
case class IntValue(override val value: Int) extends Value {
override type T = Int
}
case class StringValue(override val value: String) extends Value {
override type T = String
}
Type safe selection of Values
If we have a List of values we would like to have a type safe way of selecting all values of a specific type. Class Values and its companion object help us doing that:
object Values {
private type GroupedValues = Map[ClassTag[_ <: Value], List[Value]]
def apply(values: List[Value]): Values = {
val groupedValues: GroupedValues = values.groupBy(value => ClassTag(value.getClass))
new Values(groupedValues)
}
}
class Values private (groupedValues: Values.GroupedValues) {
// Get a List of all values of type V.
def getValues[V <: Value : ClassTag] = {
val classTag = implicitly[ClassTag[V]]
groupedValues.get(classTag).map(_.asInstanceOf[List[V]]).getOrElse(Nil)
}
def getValue[V <: Value : ClassTag] = {
getValues.head
}
def getValueOption[V <: Value : ClassTag] = {
getValues.headOption
}
def getValueInner[V <: Value : ClassTag] = {
getValues.head.value
}
}
All this works fine in both Scala 2.13 and Dotty 0.20.0-RC1 so having a list of mixed values…
val valueList = List(IntValue(1), StringValue("hello"))
val values = Values(valueList)
…we can select elements and get them returned as the correct type – all checked at compile-time:
val ints: List[IntValue] = values.getValues[IntValue]
val strings: List[StringValue] = values.getValues[StringValue]
val int: IntValue = values.getValue[IntValue]
val string: StringValue = values.getValue[StringValue]
val intOption: Option[IntValue] = values.getValueOption[IntValue]
val stringOption: Option[StringValue] = values.getValueOption[StringValue]
val i: Int = values.getValueInner[IntValue]
val s: String = values.getValueInner[StringValue]
Selecting a value as Option[T] fails in Dotty
However, if we add this function to select values as their T type (i.e. Int and String) and get it returned as an Option…
class Values ... {
...
def getValueInnerOption[V <: Value : ClassTag] = {
getValues.headOption.map(_.value)
}
}
…then things work fine in Scala 2.13:
val iOption: Option[Int] = values.getValueInnerOption[IntValue]
val sOption: Option[String] = values.getValueInnerOption[StringValue]
But in Dotty 0.20.0-RC1 this does not compile:
-- [E007] Type Mismatch Error: getValue.scala:74:29
74 | val iOption: Option[Int] = values.getValueInnerOption[IntValue]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| Found: Option[Any]
| Required: Option[Int]
-- [E007] Type Mismatch Error: getValue.scala:75:32
75 | val sOption: Option[String] = values.getValueInnerOption[StringValue]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| Found: Option[Any]
| Required: Option[String]
We can fix the problem by adding a type parameter to getValueInnerOption that ties the return type and the abstract type T together and allows us to specify the return type.
def getValueInnerOption[V <: Value {type T = U} : ClassTag, U]: Option[U] = {
getValues.headOption.map(_.value)
}
Unfortunately, this means that we will have to add the actual type of T (i.e Int or String) at the call site which is a pity because it is just boilerplate.
val iOption: Option[Int] = values.getValueInnerOption[IntValue, Int]
val sOption: Option[String] = values.getValueInnerOption[StringValue, String]
A bug in Dotty or what to do?
It seems that Dotty already knows what the upper bound of T is but cannot propagate that knowledge to the result type of the function. This can be seen if trying to ask for a String from an IntValue:
-- [E057] Type Mismatch Error: getValue.scala:75:39
75 | val wtf = values.getValueInnerOption[IntValue, String]
| ^
|Type argument IntValue does not conform to upper bound Value{T = String}
so is the original code (without type parameter U) something that can be expected to work in the final Scala 3.0 or does it need to be written in a different way?
In Dotty try match types as a replacement for type projections
type InnerType[V <: Value] = V match {
case IntValue => Int
case StringValue => String
}
trait Value {
type This >: this.type <: Value
type T = InnerType[This]
def value: T
}
case class IntValue(override val value: Int) extends Value {
override type This = IntValue
}
case class StringValue(override val value: String) extends Value {
override type This = StringValue
}
def getValueInner[V <: Value { type This = V } : ClassTag]: InnerType[V] = {
getValues.head.value
}
def getValueInnerOption[V <: Value { type This = V } : ClassTag]: Option[InnerType[V]] = {
getValues.headOption.map(_.value)
}
_.value has a dependent function type which isn't inferred by default but you can specify it:
def getValueInnerOption[V <: Value : ClassTag] = {
getValues.headOption.map((_.value): (v: V) => v.T)
}
and then
val iOption: Option[Int] = values.getValueInnerOption[IntValue]
val sOption: Option[String] = values.getValueInnerOption[StringValue]
compiles.
But the problem is that I am not sure it (and getValueInner) should work. Because the inferred return types for them involve V#T (you can see them in an error message if you give wrong return type), and trying to specify them explicitly gives
V is not a legal path since it is not a concrete type
(see http://dotty.epfl.ch/docs/reference/dropped-features/type-projection.html)

How to specify a shapeless singleton type ahead of time

I'd like to construct a type like LimitedString[Limit] where Limit is a type representation of the maximum length of the string.
It would work along the lines of
class LimitedString[Limit] [private](val s: String)
object LimitedString {
private def getLimit[Limit]: Int = ??? // turn `Limit` type into a value
def truncate[Limit](s: String) = new LimitedString[Limit](s take getLimit)
def get[Limit](s: String) =
if(s.length < getLimit) Some(new LimitedString[Limit](s))
else None
}
type Str100 = LimitedString[100] // obviously this won't work
def someLibraryMethod(s: Str100) = { ... }
What I can't figure out is how to actually type (as in keyboard) the type (as in compilation) for Limit.
I started looking into Shapeless's singleton types and found that you can say
100.narrow
// res1: Int(100) = 100
But if I try to use Int(100) as the type, I get errors.
val x: Int(100) = 100
// error: ';' expected but '(' found.
Additionally, how would I implement something like def getLimit[Limit]: Int?
I took #TravisBrown's suggestion to look into Shapless's Witness, and came up with this:
class LimitedString[Limit <: Int] private[util](val s: String) extends AnyVal {
override def toString = s
}
class LimitedStringCompanion[Limit <: Int : Witness.Aux]{
def limit: Int = implicitly[Witness.Aux[Limit]].value
def unapply(s: String): Option[LimitedString[Limit]] = {
if(s.length > limit) None else Some(new LimitedString(s))
}
def truncate(s: String): LimitedString[Limit] = new LimitedString(s take limit)
}
Usage:
import shapeless._
object MyLibraryThing {
type Name = LimitedString[Witness.`50`.T]
object Name extends LimitedStringCompanion[Witness.`50`.T]
def rename(id: Int, name: Name) = { ... }
}
The key things that make it work:
Witness.Aux is a typeclass which you can use to get the singleton value back out of the type
The singleton type Witness.`50`.T is actually a subtype of Int
Type aliases make it more convenient to interact with

Scala: retrieve type parameter from generic collection (ClassTag?)

I am defining an abstract class taking a type parameter, along with several concrete classes which set this type.
abstract class GenericFoo[T: ClassTag] {
def defaultValue: T
def useTheValue(v: T): T
}
object DoubleFoo extends GenericFoo[Double] {
def defaultValue = 42.0
def useTheValue(v: Double) = v * 2
}
object IntFoo extends GenericFoo[Int] {
def defaultValue = 1337
def useTheValue(v: Int) = v + 64
}
But when storing my Foos in a mixed collection, it seems that my type parameter T always ends up being inferred up to Any.
val fooCollection:List[GenericFoo[_]] = List(DoubleFoo, IntFoo)
for {
foo <- fooCollection
defaultValue = foo.defaultValue
result = foo.useTheValue(defaultValue)
// Error: `defaultValue` has type `Any`, cannot call `useTheValue`
} yield result
Based on several answers to similar questions, I thought using type parameter wildcard (GenericFoo[_]), and maybe ClassTag would help keep type information, but I couldn't get this example to work as I wanted.
In the snippet above, I would want foo.defaultValue to be recognized as having the correct type (T) to be applicable in foo.useTheValue(). Have I missed something?
Edit: #gzm0 was prompt to suggest that I use abstract type members. My original use-case was a little more involved, as I am also defining an abstract type for a companion object. My actual code does something similar to:
trait GenericCompanion[T] {
// Some "static" members
def defaultValue: T
def useTheValue(v: T): T
}
abstract class GenericFoo[T] {
def getCompanion: GenericCompanion[T]
// Because of this operation, it's important that we have the same type `T`
def useCompanion: T = getCompanion.useTheValue(theActualValue)
val theActualValue: T
}
object ConcreteCompanion[Double] extends GenericCompanion[Double] {
// ...
}
object ConcreteFoo[Double] extends GenericFoo[Double] {
def getCompanion = ConcreteCompanion
}
Each concrete implementation of GenericFoo[T] also comes with a CompanionFoo[T]. Concrete sub-classes of GenericFoo are supposed to instantiated, while CompanionFoo[T] objects are here to hold "static" properties and operations. In this context, it is very important that the type parameter in GenericFoo is the same as the type parameter in GenericCompanion.
(I hope this is clear enough, sorry if my example is convoluted!)
This is a vanilla example for abstract type members. Try this:
abstract class GenericFoo {
type V
def defaultValue: V
def useTheValue(v: V): V
}
object DoubleFoo extends GenericFoo {
type V = Double
def defaultValue = 42.0
def useTheValue(v: Double) = v * 2
}
object IntFoo extends GenericFoo {
type V = Int
def defaultValue = 1337
def useTheValue(v: Int) = v + 64
}
val fooCollection:List[GenericFoo] = List(DoubleFoo, IntFoo)
Rest of the code remains unchanged.
If you use an abstract type member (rather than a type parameter), Scala will take track of the concrete types it knows for a given value.
From your example explained:
val foo: GenericCollection = ???
val defaultValue = foo.defaultValue
// defaultValue: foo.V
foo.useTheValue(defaultValue)
// foo.useTheValue: (foo.V): foo.V
Although the compiler does not know what true type foo.V is, it can nevertheless figure out that the type signatures match and that using calling useTheValue with defaultValue is OK.
As far as I can tell, this would also be a perfectly OK reasoning with type parameters, Scala just does not do it.
UPDATE
You can still do this, even with the more involved example:
trait GenericCompanion {
type T
// Some "static" members
def defaultValue: T
def useTheValue(v: T): T
}
abstract class GenericFoo { self =>
type T
def getCompanion: GenericCompanion { type T = self.T }
// Because of this operation, it's important that we have the same type `T`
def useCompanion: T = getCompanion.useTheValue(theActualValue)
val theActualValue: T
}
And the concrete implementations:
object ConcreteCompanionDouble extends GenericCompanion {
type T = Double
def defaultValue: Double = ???
def useTheValue(v: Double): Double = ???
// ...
}
object ConcreteFooDouble extends GenericFoo {
type T = Double
def getCompanion = ConcreteCompanionDouble
val theActualValue: Double = ???
}

Accessing class-level values of type parameters in Scala

I have a trait and a case class implementing it. One of the features of the trait that the implementations override is a default value. I can't find a good way to access this default value from a class that is parametrized by a specific implementation of that trait.
This is minimal code, so it doesn't really demonstrate the motivation anymore, but it does demonstrate the error:
import scala.language.implicitConversions
trait Distance[T] {
val Zero: T
def +( that: T ): T
}
case class DoubleDistance( val v: Double ) extends Distance[DoubleDistance] {
val Zero = DoubleDistance( 0.0 )
def +( that: DoubleDistance ) = DoubleDistance( v + that.v )
}
object DistanceImplicits {
implicit def DoubleToDistance( v: Double ) = new DoubleDistance( v )
}
class User[T<:Distance[T]] {
val default: T = T.Zero // This line gives me a compilation error
}
The error I get is
not found: value T
When I needed to construct an Array[T] inside my User class I could get that to work by adding implicit typetag:ClassTag[T] to my arguments, but that doesn't seem to have any effect here.
First, why this doesn't work: consider a different implementation of Distance.
case class DoubleDistance1(val v: Double) extends Distance[DoubleDistance1] {
val Zero = this
def +(that: DoubleDistance1) = ??? // doesn't matter
}
What would you expect DoubleDistance1.Zero to mean? You can make it work using a "type class":
trait DistanceOps[T] {
val zero: T
def add(d1: T, d2: T): T
}
// just to let you write distance1 + distance2
implicit class RichDistance[T](d: T)(implicit ops: DistanceOps[T]) {
def +(other: T) = ops.add(d, other)
}
case class DoubleDistance(v: Double)
object DoubleDistance {
implicit object DoubleDistanceOps extends DistanceOps[DoubleDistance] {
val zero = DoubleDistance(0.0)
def add(d1: DoubleDistance, d2: DoubleDistance) = DoubleDistance(d1.v + d2.v)
}
}
// how to use
class User[T](implicit ops: DistanceOps[T]) {
val default: T = ops.zero
}
This looks like a classic use case for the type class pattern. Rather than associating an instance of the Distance trait with each value of interest, you associate one with each type of interest:
trait Distance[T] {
val Zero: T
def +( a: T, b: T ): T
}
implicit object DoubleDistance extends Distance[Double] {
val Zero = 0.0
def +( a: Double, b: Double ) = a + b
}
class User[T : Distance] {
val default: T = implicitly[Distance[T]].Zero
}
Where is that Zero supposed to come from? Are you looking to do something like this?
class User[T<:Distance[T]] {
self:Distance[T] =>
val default: T = Zero
}

Problem with specifying enumeration value types

I have a problem with specifying types for enumeration values (instances of scala.Enumeration) in functions. This originally arises from my need to serialize enumeration objects in database, but I've extracted the problematic code in the following example:
object EnumerationTypes {
class EnumerationProcessor[E <: Enumeration](enum: E, value: E#Value) {
def process: E#Value = {
value
}
}
object Enum extends Enumeration {
type EnumValue = Value
val a = Value(1, "a")
val b = Value(2, "b")
}
case class Obj(val flag: Enum.EnumValue)
def main(args: Array[String]) {
val processor = new EnumerationProcessor(Enum, Enum.a)
val obj = Obj(processor.process)
}
}
It leads to the following compilation error:
error: type mismatch;
found : EnumerationTypes.Enum#Value
required: EnumerationTypes.Enum.EnumValue
val obj = Obj(processor.process)
While this works ok:
object EnumerationTypesOk {
class EnumerationProcessor[E <: Enumeration](enum: E, value: E#Value) {
def process: E#Value = {
value
}
}
class Enum extends Enumeration {
type EnumValue = Value
val a = Value(1, "a")
val b = Value(2, "b")
}
object Enum extends Enum
case class Obj(val flag: Enum#EnumValue)
def main(args: Array[String]) {
val processor = new EnumerationProcessor(Enum, Enum.a)
val obj = Obj(processor.process)
}
}
But I don't want my code to be looks like this (first define class and then its singleton instance).
So the problem: how I can make value type be exactly the enum.EnumValue? While it seems impossible, because types cannot depend on concrete values, maybe there are some tricks to achieve desired effect with no additional boilerplate.
Edit:
Looks like you just need to help the type inferencer a bit to get your first solution working:
val processor = new EnumerationProcessor[Enum.type](Enum, Enum.a)
Hopefully someone smarter than I am will come along and explain why.
Before the OP's clarifying comment:
object EnumerationTypes {
class EnumerationProcessor[E <: Enumeration, V <: E#Value](enum: E, value: V) {
def process: V = {
value
}
}
object Enum extends Enumeration {
type EnumValue = Value
val a = Value(1, "a")
val b = Value(2, "b")
}
case class Obj(val flag: Enum.EnumValue)
def main(args: Array[String]) {
val processor = new EnumerationProcessor(Enum, Enum.a)
val obj = Obj(processor.process)
}
}
Another potential solution is to define a mixin that allows you to create a 'processor' for a particular Enumeration instance:
object EnumerationTypes {
trait EnumerationProcessor { self: Enumeration =>
def processor(value: self.Value): () => self.Value = () => {
value
}
}
object Enum extends Enumeration with EnumerationProcessor {
type EnumValue = Value
val a = Value(1, "a")
val b = Value(2, "b")
}
case class Obj(val flag: Enum.EnumValue)
def main(args: Array[String]) {
val processor = Enum.processor(Enum.a)
val obj = Obj(processor())
}
}