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]
Related
I believe my understanding on this is correct but I'd like to check. When creating typeclasses, it feels neater to have them take a single type parameter, like TypeClass[A]. If the typeclass needs to be parameterized in other ways, abstract types can be used, and there is a comparison of the two approaches here:
Abstract types versus type parameters
So far as I have been able to figure out, one thing which is not mentioned in the link is that if using a type parameter, you can witness that the parameter implements a (different) typeclass, likeso:
trait IsValidForTC[A]
abstract class TCWithTypeParam[A, B] (implicit ev: IsValidForTC[B]) {}
If I use an abstract type, I cannot be sure that it implements IsValidForTC:
abstract class TCWithAbstractType[A] (implicit ev: IsValidForTC[B]) {
type B
} //not found: Type B
If so then this makes sense, but this difference isn't mentioned in the link above so I'd like to check.
Thanks!
It's your choice whether to put implicit constraints on class level or method level. This makes impact on when the implicits are resolved.
In a type-parameter type class with implicit parameter you don't constrain the type of type class (applied to type parameters), i.e. type TCWithTypeParam[A, B] can be used even if there is no implicit IsValidForTC[B] in a scope. What you do constrain is the constructor of type class. You can emulate this behavior for type-member type class in the following way. Make the constructor private and define apply method (or instance as it's called sometimes) in companion object with desired implicit constraint
abstract class TCWithAbstractType[A] private {
type B
}
object TCWithAbstractType {
def apply[A, _B: IsValidForTC]: TCWithAbstractType[A] { type B = _B } =
new TCWithAbstractType[A] { type B = _B }
}
You can add the witness, but it needs to be inside the class scope so it has access to B:
abstract class TCWithAbstractType[A] {
type B
implicit val ev: IsValidForTC[B]
}
But in practice this is often less convenient than the type parameter, because it has to be implemented explicitly, something like
new TCWithAbstractType[A] {
type B = ...
implicit val ev: IsValidForTC[B] = ...
}
while a constructor parameter just gets the implicit value from outer scope.
Note: this is a partial duplicate of my answer to your follow-up question, but left here in case someone stumbles on this question first.
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]
I needed a type Union to force restriction on types, So as per answer in here, I defined my Union as:
sealed trait Value[T]
object Value{
implicit object NumberWitness extends Value[Int]
implicit object StringWitness extends Value[String]
}
Now, How do i create a list or class parameterized by this type union? Is it possible to do so?? I tried following syntax in repl, but without any luck:
scala> import Value._
import Value._
scala> def list[V: Value] = List("hello", 1)
list: [V](implicit evidence$1: Value[V])List[Any]
scala> list
<console>:18: error: ambiguous implicit values:
both object NumberWitness in object Value of type Value.NumberWitness.type
and object StringWitness in object Value of type Value.StringWitness.type
match expected type Value[V]
list
^
Or Is it possible to do so with advanced FP libraries like scalaz or cats??
This is called type class, not type union. And they are intended to allow you to write methods which work either with Int or with String, e.g.
def listOfValues[V: Value](x: V) = List(x)
listOfValues(1) // works
listOfValues("") // works
listOfValues(0.0) // doesn't work
listOfValues(1, "") // doesn't work
not to allow mixing different types.
You can do it using existential types, e.g.
case class WithValue[V: Value](x: V)
object WithValue {
implicit def withValue[V: Value](x: V) = WithValue(x)
}
def list = List[WithValue[_]]("hello", 1)
but I would not recommend actually doing that. There is quite likely a better way to solve your problem.
In particular, consider using simply
// not sealed if you need to add other types elsewhere
// can be Value[T] instead
sealed trait Value
case class IntValue(x: Int) extends Value
case class StringValue(x: Int) extends Value
// add implicit conversions to IntValue and StringValue if desired
List(StringValue("hello"), IntValue(1))
What is wrong with the code below? I'm getting the following complaint from the compiler on the indicated line: type arguments [Asset] do not conform to trait Worker's type parameter bounds [T <:
br.Doable]
How is this so? Worker expects a subtype of Doable, and asset extends Doable.
trait Doable
trait Worker[T<:Doable] {
def hey():String
}
case class Asset() extends Doable
case class Hey[Asset] extends Worker[Asset] { // << error here
def hey() = "You!"
}
When you declare case class Hey[Asset], you bind a new type variable Asset, you do not refer to case class Asset() extends Doable (you are shadowing the Asset type variable).
Your code is equivalent to :
case class Hey[A] extends Worker[A] {
...
}
which obviously won't work.
The problem is you have confused yourself by using the same value, Asset, to refer to a case class and a type parameter.
You probably intend to do something like this:
case class Hey[T](str: String) extends Worker[Asset] {
def hey() = "You!"
}
Though it is beside the point, note that I added a parameter to Hey because case classes without parameters have been deprecated.
This has been asked a lot of times, and I think that the confusion can easily go away if you see the analogy between type parameters and constructor parameters, thinking about them just as different kinds of constructor parameters: type-level and value-level.
Disclaimer Of course, this is only an analogy and it will break at a lot of different levels and there are a lot of corner cases as with anything Scala; but my point here is that it can be useful
At the type level, you can think of <: as a the equivalent of : at the value level:
class TypeParamsVsVals {
type X
type X1 <: X
class Buh[T <: X]
class Oh[T1 <: X1] extends Buh[T1]
// wait for Scala 3
// class Oh[T1 <: X1] extends Buh[T = T1]
type x
type x1 <: x
class buh(val t: x)
class oh(val t1: x1) extends buh(t = t1)
}
What I think is the main source of confusion is that at the type level there's no kind distinction between the two sides of <:, and to make things worse you can write T without any (no pun intended) bound, while you cannot do the same at the value level:
class NoBounds[T]
// same as
class AltNoBounds[T <: Any]
// you cannot write
// class noBounds(val t)
class noBounds(val t: Any)
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