Hierarchy of value classes in Scala? - scala

I have defined the following class hierarchy where I want to restrict the the type parameter to be conformable with Double...
sealed abstract class Quantity[-T](value: T)(implicit ev: T <:< Double)
case class DiscreteQuantity(value: Long) extends Quantity[Long](value)
case class ContinuousQuantity(value: Double) extends Quantity[Double](value)
...is it possible to re-write the above hierarchy so that the concrete types are value classes? From the docs I know that value classes can not be extended, so that rules out having Quantity inherit from AnyVal. In order for concrete classes to inherit from AnyVal I need to make Quantity a trait, which is fine, but then I lose the contra-variant annotation on the type parameter.
Thoughts?

It is possible, but as I said in the comment: <:< and <: don't include weak conformance, so basically only Quantity[Double] can exist.
sealed trait Quantity[-T <: Double] extends Any {
protected[this] def value: T
}
case class ContinuousQuantity(value: Double) extends AnyVal with Quantity[Double]

Related

Generic value class

Is it possible to define value class in scala for some Numeric[T]? I tried something like this:
case class Inches[T <: Numeric[T]](value: T)(implicit num: Numeric[T]) extends AnyVal
However I get compilation error that value classes can have only one parameter.
Is there any way to bypass this?
Thanks beforehand.
You can move the Numeric parameter from class to the methods which require it:
case class Inches[T](value: T) extends AnyVal {
def foo()(implicit num: Numeric[T]) = ...
}
(T <: Numeric[T] is wrong.)

Self-type does not conform to base class

Using the following code:
trait Hello[B <: Baz[_, _]]
trait World[F <: Foo] { self: Hello[Baz[F, _]] =>
def foo: F
}
trait Baz[F <: Foo, B <: Bar]
trait Foo
trait Bar
case class BasicHello() extends Hello[BasicBaz] with World[BasicFoo]
case class BasicBaz() extends Baz[BasicFoo, BasicBar]
case class BasicFoo() extends Foo
case class BasicBar() extends Bar
I get the following error in my IDE at the BasicHello case class:
Illegal inheritance, self-type BasicHello does not conform to Hello[Baz[F, _]]
I have no clue why the compiler doesn't allow this since BasicHello extends Hello[BasicBaz], which in its turn extends Baz[BasicFoo, BasicBar]. The type of BasicHello should be Hello[Baz[Foo, Bar]] which is exactly what the World mix-in requires. Is there some hierarchical property I'm missing of self-types?
The problem is that Hello is invariant in B. So, Hello[BasicBaz] is not a subclass of Hello[Baz[BasicFoo,BasicBar]] even though BasicBaz is a subclass of Baz.
If you make Hello covariant in B, (trait Hello[+B <: Baz[_,_]]), then it'll compile.

Scala - Using complicated generics to get information of a subclass and another class - doesn't compile?

I am writing a program in scala that uses a framework around:
trait Tool[T <: Tool[T, U], U <: Settings[T]] {
// members here
def createSettingsFrom(settingsWithStringNames: Map[String, _]): U
}
trait Settings[T <: Tool[T, _ <: Settings[T]]
In Tool, T is the subclass, and U is a class that carries information for it. Each Tool can be regarded as a sort of command with parameters, and those parameters are custom for each of them.
I also have a class that extends it, along with its "information carrier":
object Cleanup extends Tool[Cleanup, CleanupSettings] {
override def createSettingsFrom(settings: Map[String, _]): CleanupSettings
= CleanupSettings(
settings.get("attribute1").asInstanceOf[Int]
settings.get("attribute2").asInstanceOf[String])
}
case class CleanupSettings extends Settings[Cleanup](
//attribute1: Int,
//attribute2: String
//more attributes)
When I attempt to compile these classes, I get the following stacktrace:
Information:21/10/16 03:20 - Compilation completed with 2 errors and 0 warnings in 3s 200ms
/project_folder/src/main/scala/io/oreville/maptools/operations/cleanup/Cleanup.scala
Error:(17, 24) type arguments [package.tools.operations.cleanup.Cleanup,package.tools.operations.cleanup.CleanupSettings] do not conform to trait ConfigurableTool's type parameter bounds [T <: package.tools.ConfigurableTool[T,U],U <: package.tools.params.Settings[T]]
object Cleanup extends ConfigurableTool[Cleanup, CleanupSettings] {
^
/project_folder/src/main/scala/io/oreville/maptools/operations/cleanup/CleanupSettings.scala
Error:(11, 11) type arguments [package.tools.operations.cleanup.Cleanup] do not conform to trait Settings's type parameter bounds [T <: package.tools.Tool[T, _ <: package.tools.params.Settings[T]]]
extends Settings[Cleanup]
^
I also have a trait ConfigurableTool which is just an extension of Tool with some extra functionality, so it has the exact same generic signature and it just extends Tool[T, U].
I tried multiple things to solve the problem including adding combinations of + and - to my generics for co- and contravariance, but it doesn't help.
I did consider using a Dynamic type for my settings, but speed is a bit of a factor. I don't even know if it would solve the problem if I did.
That's it really, I hope that you have some time to help my case, if not, thanks for reading anyway!
I wan't able to reproduce the error message you got.
There are a few typos in the code, but after cleaning them up, the only error left is in the definition of Cleanup which you cannot pass as a type parameter when extending a trait.
object Cleanup extends Tool[Cleanup.type, CleanupSettings] { ... }
illegal cyclic reference involving object Cleanup
You can get around that by making it a trait, and extending it in the companion object. Here's the full code:
trait Tool[T <: Tool[T, U], U <: Settings[T]] {
// members here
def createSettingsFrom(settingsWithStringNames: Map[String, _]): U
}
trait Settings[T <: Tool[T, _ <: Settings[T]]]
trait Cleanup extends Tool[Cleanup, CleanupSettings] {
override def createSettingsFrom(settings: Map[String, _]): CleanupSettings = CleanupSettings(
settings.get("attribute1").asInstanceOf[Int],
settings.get("attribute2").asInstanceOf[String])
}
object Cleanup extends Cleanup
case class CleanupSettings(
attribute1: Int,
attribute2: String) extends Settings[Cleanup]

Type aliasing type constraints in generics

I have a situation where I want to use a bounded generic type as a constraint on what class can be produced. The problem is that I need to
abstract class SomeAbstractClass
trait Foo[A <: SomeAbstractClass]
trait Bar[A] extends Foo[A]
// Fails error: type arguments [A] do not conform to trait Foo's type parameter bounds [A <: SomeAbstractClass]
// Need to write it like this, for every single subclass of Foo
trait Bar[A <: SomeAbstractClass] extends Foo[A]
Is there an easier way to promote that through the system without having to retype the bounds every time?
Constraints on type parameters are constraints. They don't propagate transitively via inheritance as you would like them to.
Perhaps this is applicable or produces some new ideas at least:
abstract class SomeAbstractClass
trait Foo { // propagated by abstract type member
type A <: SomeAbstractClass
}
trait Bar extends Foo // no generic type parameter needed here
trait BAR[SAC <: SomeAbstractClass] extends Bar { type A = SAC } // introduce type parameter
trait Baz[SAC <: SomeAbstractClass] extends BAR[SAC] // generic type parameter needed here

Typeclass subclassing

I want to limit a parameter of union type of A and B types, where B is some general type, that will be subtyped. I want to put the objects in this method:
def accept[A](a:A)(implicit ev:FooOrBaish[A]){ /* do something */}
This is, how do I specify the implicits:
case class Foo(i:Int)
trait Baish
case object Bar extends Baish
case class Baz(x:String) extends Baish
class FooOrBaish[A]
object FooOrBaish{
implicit object FooWit extends FooOrBaish[Foo]
implicit object BaishWit extends FooOrBaish[Baish]
}
Now, I can put in accept Foo(5), but cannot put there Baz("a") neither Bar, the compiler screams: error: could not find implicit value for parameter ev: FooOrBaish[Baz].
Where can I specify the subtype relation?
Change FooOrBaish's type to be contravariant and it works
class FooOrBaish[-A]