I have the following problem with hierarchy of traits in Scala code:
First of all, I have a basic trait MyTrait[A] with such definition:
trait MyTrait[A] {
def v1: A
}
It is then followed by a definition of a trait Base with a type-member:
trait Base[A] {
type T <: MyTrait[A]
val baseV: T
}
And, at last, a trait Gen which overrides Base's type member.
trait Gen[A, X <: MyTrait[A]] extends Base[A] {
type T = X
}
The problem is that in the Gen trait it seems that bounds of the type-member are lost. This can be proven by following tests:
Compiles:
trait Test1 {
val x: Base[_]
println(x.baseV.v1)
}
Doesn't compile (value v1 is not a member of Test2.this.x.T):
trait Test2 {
val x: Gen[_, _]
println(x.baseV.v1)
}
I would like to know whether it's a limitation of the language or there is a workaround it. Questions on similar topics on stackowerflow (1, 2) appear to be focusing on different aspects than mine and I am genuinely at a loss because I can't find much information about such behavior in Scala.
Scala code template of this question can be found on scastie
This works:
trait Test2 {
val x: Gen[A, X] forSome { type A; type X <: MyTrait[A] }
println(x.baseV.v1)
}
I believe the issue is that
Gen[_, _]
Has to mean
Gen[_ >: Nothing <: Any, _ >: Nothing <: Any]
Which is the same as
Gen[A, X] forSome { type A; type X }
That is, even though the bounds on Gen say that X <: MyTrait[A], the wildcards do not inherit that bound. You can see a similar problem here:
trait Data { def data: String }
trait Box[A <: Data] { def data: A }
def unbox(b: Box[_]): String = b.data.data // nope; the wildcard is not <: Data
We can add the bounds to the wildcards explicitly. However, because the bound on the second wildcard depends on the first one, we are forced to use the extended forSome syntax for the existential, so we can name A and use it twice.
Gen[A, _ <: MyTrait[A]] forSome { type A }
And I opted to just put everything in the existential clause, which is equivalent:
Gen[A, X] forSome { type A; type X <: MyTrait[A] }
You can also use
Gen[_, _ <: MyTrait[_]]
but this is not equivalent, as it doesn't relate the left and right parameters. If Gen[A, _] contained an A in addition to a MyTrait[A], then using x: Gen[_, _ <: MyTrait[_]] would render the "bare" value and the "wrapped" value with incompatible types.
Related
I have a somewhat complex typeclass situation in the following format:
sealed trait TypeClass[S <: MyType] {
type Out <: MyType
}
sealed trait LowPriorityTypeClass {
// Case: OtherTypeClass is NOT defined for the input type S.
// The output type is the same as the input type.
implicit def default[S <: MyType]: TypeClass.Aux[S, S] = ???
}
object TypeClass extends LowPriorityTypeClass {
type Aux[S <: MyType, O <: MyType] = TypeClass[S] { type Out = O }
// Case: OtherTypeClass is defined for the input type S.
// The output type is the same as in the OtherTypeClass definition.
implicit def hasOtherTC[S <: MyType, O <: MyType](
implicit otherTC: OtherTypeClass.Aux[S, O],
): TypeClass.Aux[S, O] = ???
}
The default definition was put in the LowPriorityTypeClass trait with the intention of having a lower priority. However, an ambiguity with hasOtherTC still happens for some type S, apparently because the declaration of default is more specific than the declaration of hasOtherTC for that type S.
Is there a general way to ensure that an implicit definition will always have a higher/lower priority than other definition? (My question is not for the specific code above.)
Let me know if posting a more complete sample code would help.
Please see Why is this implicit ambiguity behaviour happening? including comments.
There is no sense in introducing trait LowPriorityTypeClass in this case because anyway implicit default is more specific than hasOtherTC.
There is no general way. You can use type classes Not (shapeless.Refute, implicitbox.Not) or shapeless.LowPriority, implicitbox.Priority or library https://github.com/milessabin/export-hook.
object TypeClass {
type Aux[S <: MyType, O <: MyType] = TypeClass[S] {type Out = O}
implicit def hasOtherTC[S <: MyType, O <: MyType](implicit
otherTC: OtherTypeClass.Aux[S, O]
): TypeClass.Aux[S, O] = ???
implicit def default[S <: MyType](implicit
noOtherTC: Refute[OtherTypeClass[S]]
): TypeClass.Aux[S, S] = ???
}
I encounter quite often the following issue in Scala :
Given a trait
trait Foo { def foo: String }
and a parameterized class
case class Bar[T <: Foo](t: T)
I'd like to write a method that work with a Bar without duplicating the type constraint, something like :
def doSth(bar: Bar[_]) = bar.t.foo
Unfortunately, it doesn't compile and I need to write :
def doSth[T <: Foo](bar: Bar[T]) = bar.t.foo
Why the compiler can't infer that if I have a Bar[_], the _ must be a Foo ?
Is there a workaround (abstract type will avoid the duplication, but it will add complexity to represent some constraints) ?
It seems as if
def doSth(bar: Bar[_]) = bar.t.foo
is essentially the same as
def doSth0(bar: Bar[X] forSome { type X }) = bar.t.foo
and type X is just completely unconstrained. In my opinion, the question should therefore be rather:
Why does the compiler allow something like Bar[X] forSome { type X } at all, even though X is not declared as subtype of Foo, whereas Bar requires the argument to be subtype of Foo?
I don't know an answer to that. Probably it again has something to do with java generics.
Workarounds
Given the trait and class
trait Foo { def foo: String }
case class Bar[T <: Foo](t: T)
the following two definitions work without an additional type parameter:
def doSth1(bar: Bar[X] forSome { type X <: Foo }) = bar.t.foo
def doSth2(bar: Bar[_ <: Foo]) = bar.t.foo
Another option would be to constrain the type of t in Bar itself:
case class Bar2[T <: Foo](t: T with Foo)
def doSth3(bar: Bar2[_]) = bar.t.foo
I came across the existential quantification for F-bounded types while trying to understand scala's type system.
Let A be a type
trait A[F <: A[F]] { self: F => }
where F is the F-bounded self-type of A.
And B some subtype of A
case class B() extends A[B]
If I try to declare a List of A with existential quantification
val a: List[T] forSome { type T <: A[T] }
I get an error when assigning a a List of B. For some reason scala infers the type List[A[Nothing]].
val a: List[T] forSome { type T <: A[T] } = List(B()) // type mismatch
When applying the simplification rule 4 (as described in the scala-spec) the covariant occurrence of T may be replaced by the upper bound (here: A[T])
val b: List[A[T]] forSome { type T <: A[T] } = List(B())
Both examples should be equivalent (if I have not misunderstood something), but the simplified one works just fine. Furthermore the following is also correct and should be equivalent:
val c: List[T forSome { type T <: A[T] }] = List(B())
But for some reason the type of c and the type of b are not equal.
So my question is: Is this a bug of the scala compiler or did I misunderstand something crucial?
Edit: Is my assumption correct, that the types of c and b are not equal, because the existential quantification in c does not see the covariant occurrence of T?
I have a generic trait MappingPath, invariant regarding it's type parameters:
trait MappingPath[X<:AnyMapping, Y<:AnyMapping]
and an interface of a factory for it:
trait Pathfinder[X, Y] {
def apply(fun :X=>Y) :MappingPath[_<:AnyMapping,_<:AnyMapping]
def get(fun :X=>Y) :Option[MappingPath[_<:AnyMapping, _<:AnyMapping]]
}
I start a skeleton implementation which works for a single mapping:
class MappingPathfinder[M<:AnyMapping, X, Y] extends Pathfinder[X, Y] {
override def apply(fun :X=>Y) :MappingPath[M, _<:AnyMapping] = ???
override def get(fun :X=>Y) :Option[MappingPath[M, _<:AnyMapping]] = ???
}
which produces a compile error complaining that MappingPathfinder.apply overrides nothing and doesn't implement Pathfinder.apply. What's interesting, replacing M with _<:AnyMapping in apply's return type makes it compile, and no complaints are made regarding similar get method.
What's going on? I use scala 2.11.5.
EDIT:
I was able to circumvene my problem by adding explicit existantial annotations:
//Pathfinder
def apply(fun :X=>Y) :MappingPath[A, B] forSome { type A<:AnyMapping; type B<:AnyMapping }
//MappingPathfinder
def apply(fun :X=>Y) :MappingPath[A, B] forSome { type A>:M<:M; type B<:AnyMapping }
It seems to work, i.e
I can do:
(p :MappingPath[_<:AnyMapping, M]) ++ mappingPathfinder(f),
where ++ requires a path starting with the exact same type as this ends. It looks a bit silly and certainly confusing though.
Not an answer, but your use case can be simplified to:
trait Higher[U]
trait Super {
def foo: Higher[_]
}
trait Sub[M] {
override def foo: Higher[M] // error: method foo overrides nothing
}
Instead of existential types, I would use a type member:
trait Super {
type U
def foo: Higher[U]
}
trait Sub[M] {
type U = M
}
I think the difference is that in the case of the existential type, you only specify that the type parameter returned has some upper bound, but not necessarily that it is always the same type; whereas in my second example, type U means this will eventually be one specific type, and you can only refine a specific type. You can make upper bounds more precise:
trait Upper
trait A {
type U <: Upper
}
trait Specific extends Upper
trait B extends A {
type U <: Specific // type is "overridden"
}
If it's possible, I would avoid existential types, and your case seems a perfect fit for such avoidance. Most of the time, existential types are only needed for Java interop.
Assume the existence of the following types and method:
trait X[A <: X[A]]
case class C extends X[C]
def m(x: PartialFunction[X[_], Boolean])
I want to be able to create a PartialFunction to be passed into m.
A first attempt would be to write
val f: PartialFunction[X[_], Boolean] = {
case c: C => true
}
m(f)
This fails with type arguments [_$1] do not conform to trait X's type parameter bounds [A <: X[A]]. So, it seems we have to constraint X's type parameters.
A second attempt:
val f: PartialFunction[{type A <: X[A]}, Boolean] = {
case c: C => true
}
m(f)
This fails on the application of m because PartialFunction[AnyRef{type A <: X[this.A]},Boolean] <: PartialFunction[X[_],Boolean] is false.
Is there any way not involving casting that actually satisfies the compiler both on the definition of the partial function and on the application of m?
I'm not sure what exactly you want, but since you are using an existential type (in disguise of the _ syntax), this is how you can make that work:
val f: PartialFunction[X[A] forSome {type A <: X[A]}, Boolean] = {
case c : C => true
}
The _ syntax isn't good enough here, since you need to give the existential type the right upper bound. That is only possible with the more explicit forSome syntax.
What I find surprising, though, is that Scala accepts the declaration
def m(x: PartialFunction[X[_], Boolean])
in the first place. It seems weird that it even considers X[_] a well-formed type. This is short for X[A] forSome {type A <: Any}, which should not be a valid application of X, because it does not conform to the parameter bounds.
Not sure if that's what you wanted to achieve, but this is the working sequence:
trait X[A <: X[A]]
case class C extends X[C]
def m[T<:X[T]](x: PartialFunction[X[T], Boolean]) = print("yahoo!")
scala> def f[T<:X[T]]:PartialFunction[X[T], Boolean] = {
| case c: C => true
| }
f: [T <: X[T]]=> PartialFunction[X[T],Boolean]
scala> m(f)
yahoo!