Scala multiple type conformance - scala

I have the following code and will like to call a method on top of a class that implements the trait EventTraces[T] and at the same time T should implement the trait Event (e.g as in doSomethingOnTopOfAnEventTrace())
trait Event
class ConcreteEvent[T <: Event]
trait EventTrace[T <: Event]
class ConcreteEventTrace[T <: Event] extends EventTrace[T]
val concreteEventTrace : ConcreteEventTrace[ConcreteEvent] = new ConcreteEventTrace(new ConcreteEvent)
def doSomethingOnTopOfAnEventTrace[T <: Event, Z <: EventTrace[T]](eventTrace: Z) {
println("Action on top of a Any kind of EventTrace of any type of Event")
}
However calling doSomethingOnTopOfAnEventTrace(concreteEventTrace) gives me the following error:
Error:(129, 3) inferred type arguments [Nothing,ConcreteEventTrace[ConcreteEvent]] do not conform to method doSomethingOnTopOfAnEventTrace type parameter bounds [T <: Event,Z <: EventTrace[T]]
doSomethingOnTopOfAnEventTrace(concreteEventTrace)
^
Error:(129, 38) type mismatch;
found : ConcreteEventTrace[ConcreteEvent]
required: Z
doSomethingOnTopOfAnEventTrace(concreteEventTrace)
^

The issue is that Scala can't infer T here because it only has Z in the arguments (I don't see a reason it couldn't, in this case). The obvious way to fix it is doSomethingOnTopOfAnEventTrace[ConcreteEvent, ConcreteEventTrace[ConcreteEvent]](concreteEventTrace). Another, if you don't actually need to be generic in Z:
def doSomethingOnTopOfAnEventTrace[T <: Event](eventTrace: EventTrace[T])
You could also try to use type members instead of generics in Event and EventTrace.

Related

Generic nested type inference works with arity-2 but not with currying

Trying to figure out why the code compiles for nested type inference on method with arity-2 but not with currying.
object Test
{
trait Version
object VersionOne extends Version
trait Request[A <: Version]
trait RequestOne extends Request[VersionOne.type]
case class HelloWorld() extends RequestOne
def test1[A <: Version, T <: Request[A]](t : T, a : A): T = t
def test2[A <: Version, T <: Request[A]](t : T)(a : A): T = t
}
// This works
Test.test1(Test.HelloWorld(), Test.VersionOne)
// This doesn't
Test.test2(Test.HelloWorld())(Test.VersionOne)
test2 fails to compile with the following error:
Error:(22, 73) inferred type arguments [Nothing,A$A96.this.Test.HelloWorld] do not conform to method test2's type parameter bounds [A <: A$A96.this.Test.Version,T <: A$A96.this.Test.Request[A]]
def get$$instance$$res1 = /* ###worksheet### generated $$end$$ */ Test.test2(Test.HelloWorld())(Test.VersionOne)
Looking forward to some insights on the same.
#DmytroMitin already explained why it does fail.
However, you can solve the problem this way, using the Partially-Applied Type trick, together with Generalized Type Constraints.
def test2[T](t: T): Test2PartiallyApplied[T] = new Test2PartiallyApplied(t)
final class Test2PartiallyApplied[T](private val t: T) extends AnyVal {
def apply[A <: Version](a: A)(implicit ev: T <:< Request[A]): T = t
}
Which you can use like this.
Test.test2(Test.HelloWorld())(Test.VersionOne)
// res: HelloWorld = HelloWorld()
Nothing in compile error usually means that some type parameters were not inferred.
Try to specify them explicitly
Test.test2[Test.VersionOne.type, Test.RequestOne](Test.HelloWorld())(Test.VersionOne)
Difference between test1 and test2 is not only in currying. For example, generally in test2(t: ...)(a: ...) type of a can depend on value of t. So for test2 type inferrence is more complicated than for test1.
Scala type inference and multiple arguments list
Type inference with type aliases and multiple parameter list function
Multiple parameter closure argument type not inferred
What's the difference between multiple parameters lists and multiple parameters per list in Scala?

Scala F-Bounded Type Polymorphism

trait Account[T <: Account[T]]
case class BrokerAccount(total:BigDecimal) extends Account[BrokerAccount]
case class SavingsAccount(total:BigDecimal) extends Account[SavingsAccount]
Below function declaration and invocation works fine.
def foo1( xs: Array[T forSome { type T <: Account[T] }]):Array[T forSome { type T <: Account[T] }] = xs
foo1(Array(BrokerAccount(100),SavingsAccount(50)))
But below invocation gives compilation error.
def foo2( xs: List[T forSome { type T <: Account[T] }]):List[T forSome { type T <: Account[T] }] = xs
foo2(List(BrokerAccount(100),SavingsAccount(50)))
Error
Main.scala:14: error: type mismatch;
found : List[Product with Serializable with Main.Account[_ >: Main.SavingsAccount with Main.BrokerAccount <: Product with Serializable with Main.Account[_ >: Main.SavingsAccount with Main.BrokerAccount <: Product with Serializable]]]
required: List[T forSome { type T <: Main.Account[T] }] foo2(List(BrokerAccount(100),SavingsAccount(50)))
Can someone please explain me why compilation error occur in later case?
The key to the problem is variance - you're trying to return a contravariant value in covariant position (function return type).
Despite List type is covariant in its argument (trait List[+A]), this essentially means its values are contravariant (can be assigned to a List of supertypes):
val listOfSupers: List[_ >: Account[_]] = List(BrokerAccount(100), SavingsAccount(50))
What you're trying to return from the function foo2 is a complete contrary - List[_ <: Account[_]], thus the compiler error.
If instead of List you use Set there, which is invariant in its type parameter just like Array, everything will work fine.

Scala type bounds "single-levelness"

Let's have a hierarchy defined like this:
trait Upper
case class Error(msg: String) extends Upper
Trying to define a trait like this:
trait Mixin[T <: Upper] {
def compute[S <: T](param: String, obj: S)
def use = compute("hello", Error("world"))
}
Causes compilation error:
error: inferred type arguments [Error] do not conform to method compute's type parameter bounds [S <: T]
def use = compute("hello", Error("world"))
^
error: type mismatch;
found : Error
required: S
def use = compute("hello", Error("world"))
^
Can someone explain me why that happens, and how to workaround it?
Update
The type bounds can't be removed. They are necessary because of some implicits that won't work otherwise.
I'm not sure what you're trying to do with this, but the type parameter T on Mixin is an invariant with an upper-bound of Upper. That means that we know it is some sub-type of Upper, but we don't know which--which means we don't know that it has type Error. And we can't know that Error is a sub-type of T.
If you introduce a third type case class A extends Upper, it becomes more clear why this can't work.
T might be A, and if S <: A, then S = Error <: T = A is impossible.
How can you work around this? Remove the type bounds. It's not clear why you have them in the first place.

Kinds not conforming with type lambda

Having trouble with type "kinds":
trait Sys[ S <: Sys[S]]
trait Expr[S <: Sys[S], A]
trait Attr[S <: Sys[S], A[_]]
def test[ S <: Sys[S]]: Attr[S, ({type l[x<:Sys[x]]=Expr[x,Int]})#l] = ???
This fails with
error: kinds of the type arguments (S,[x <: Sys[x]]Expr[x,Int]) do not conform
to the expected kinds of the type parameters (type S,type A) in trait Attr.
[x <: Sys[x]]Expr[x,Int]'s type parameters do not match type A's expected parameters:
type x's bounds <: Sys[x] are stricter than type _'s declared bounds >: Nothing <: Any
def test[S <: Sys[S]]: Attr[S, ({type l[x<:Sys[x]]=Expr[x,Int]})#l] = ???
^
What's the problem with the declared bounds? Do I need to carry that cr*ppy partially applied type into the type constructor of trait Attr? And why? Can I fix this without touching the definition of Attr?
I do need the bounds in function test in order for the implementation to work, but I do not want to proliferate those bounds to the public interface Attr.
Note: If I use a type member (what I don't want), it works:
trait Attr[S <: Sys[S]] { type A[_]}
def test[ S <: Sys[S]]: Attr[S] { type A[S <: Sys[S]] = Expr[S, Int]} = ???
As you observed, you can't always mismatch bounds when providing a higher-kinded type argument. Interestingly, it is actually a variance issue:
class A
class B extends A
trait NeedsNeedsA[T[S <: A]]
trait NeedsNeedsB[T[S <: B]]
trait NeedsA[S <: A]
trait NeedsB[S <: B]
def x: NeedsNeedsA[NeedsB] // fails
def y: NeedsNeedsB[NeedsA] // works
which makes sense if you think of a higher-kinded type as a function on types, contravariant in its argument's bound.
Interestingly, in a structural type, which is on the surface a lot like subtyping, Scala does not give the same error:
def t: MemberNeedsA { type T[S <: B] }
def u: MemberNeedsB { type T[S <: A] }
the reason being that a structural type is sort of like an intersection:
def s: MemberNeedsA with MemberNeedsB
and it may be that that intersection cannot actually exist in nature, but Scala doesn't check that.
OK, but that's not so relevant to your question. Back to your question: I think you have a variance issue. You want test to give the caller back an Attr, and an Attr posseses a type function (A[_]), and you want to say, this Attr has a type function that requires a more specific argument. I think you can see why you shouldn't be allowed to do that -- it's the same reason you can't substitute a function requiring a more specific argument in place of one that would require a more general argument.
At this point I'm afraid the solution will have to depend on what you want Attr to be able to accomplish. You need to figure out why you need to restrict the type argument in some cases more than others. If it makes conceptual sense in your program that "some Attrs are more restrictive than others", you could define:
trait Attr[S <: Sys[S], B[Y <: B[Y]], A[X <: B[X]]]
def test[S <: Sys[S]]: Attr[S, Sys, L] = ...
But the solution will depend on what the restriction on A[_]'s argument is intended to mean.

How can I pass an object in a function as an argument in Scala?

Given two objects which extend a class:
object PageDAO
extends SalatDAO[Page, Long](collection=Config.getMongoDB("db_development")("pages"))
object BookDAO
extends SalatDAO[Book, Long](collection=Config.getMongoDB("db_development")("books"))
I want to write a function which has the object as a parameter:
def find[ID](salatDAO:SalatDAO[Product,ID]) = salatDAO.find(MongoDBObject()).limit(10)
Like
scala> find[Long](PageDAO)
<console>:27: error: type mismatch;
found : PageDAO.type (with underlying type object PageDAO)
required: com.novus.salat.dao.SalatDAO[Product,Long]
Note: Page <: Product (and PageDAO.type <: com.novus.salat.dao.SalatDAO[Page,Long]), but class SalatDAO is invariant in type ObjectType.
You may wish to define ObjectType as +ObjectType instead. (SLS 4.5)
find[Long](PageDAO)
Is this possible?
Just follow the advice of the compiler. If you want SalatDAO[A, _] to be a subclass of SalatDAO[B, _] when A <: B (i.e., when A is a subclass of B), declare SalatDAO to be covariant in its first parameter:
trait SalatDAO[+A, B] // ...
^ <-- that plus does the trick
If you cannot change the variance annotation, you can use usage-site bounds, à la Java, as Eastsun proposed in the comments:
def find[P <: Product, I](salatDAO: SalatDAO[P,I]) = // ...