Using a type constructor in a type refinement - scala

I'm having a problem which is probably best expressed in code - a simplified example below:
abstract class MainTC[A] {
type E
// A type constructor for the implementing type:
type CN[_]
implicit val ev: CN[A] =:= A // check that CN works as a type constructor for A
def get(self: A): E
def set[B](self: A, other: B): CN[B] { type E = B }
def convert[B](self: A)(implicit conv: Convert[A, E, B]) = conv.convert(self)(this)
}
abstract class Convert[A, _E, B] {
type Out
def convert(self: A)(implicit isMain: MainTC[A] { type E = _E }): Out
}
object Convert {
implicit def convertDoubleToInt[A, _CN[_]](implicit
isMain: MainTC[A] { type E = Double; type CN[_] = _CN[_] },
): Convert[A, Double, Int] = new Convert[A, Double, Int] {
type Out = _CN[Int] { type E = Int }
def convert(self: A): Out = {
val toInt = isMain.get(self).toInt
isMain.set[Int](self, toInt)
// type mismatch -
// found: isMain.CN[Int]{type E = Int} (which expands to) _CN[_]{type E = Int}
// required: this.Out (which expands to) _CN[Int] {type E = Int}
}
}
}
The basic situation here is quite simple - I am using a typeclass to implement the polymorphic convert function. The tricky part is that I am storing a type constructor as an abstract type within the MainTC typeclass. When converting in the Convert typeclass, I would then like to use that type constructor to create a new type as the output type (eg, CN[Int]). I am trying to use something like the Aux pattern to achieve this, with _CN[_] being created as a type alias for isMain.CN[_]. However, it's not working (error message in the code). If anyone could lend me a hand I'd be most grateful.

do you mean type CN[_] = _CN[_] or type CN[X] = CN[X]? If you change it to the latter, you run into the issue that
def convert(self: A): Out
can't implement
def convert(self: A)(implicit isMain: MainTC[A] { type E = _E }): Out
because it's missing the implicit parameter. Keep in mind that Scala implicits don't have to be coherent: convertDoubleToInt's (isMain: MainTC[A] {type E = _ E}).TC isn't the same as convert's (isMain: MainTC[A] {type E = _ E}).TC

Related

T <: A, return T method

here is some sample code:
trait A
trait B extends A
def test[T <: A](): T = {
new B {}
}
but I get an compile error:
type mismatch;
found : B
required: T
new B {}
how to make it working ?
( but without doing asInstanceOf[T] at the end )
thanks!
The signature of your method
def test[T <: A](): T
promises that for any type T that is a subtype of A you return a value of this type T. And then you returned a value of type B. You violated the signature (there are many subtypes of A, not only B).
For example you can try a type class (in this way you say that you return a value of T not for any T <: A at all but for any T <: A that can be handled by the type class MakeT)
def test[T <: A]()(implicit makeT: MakeT[T]): T = makeT.makeT()
trait MakeT[T] { // or MakeT[+T]
def makeT(): T
}
object MakeT {
implicit val bMakeT: MakeT[B] = () => new B {}
// or implicit def bMakeT[T >: B]: MakeT[T] = () => new B {}
}
test[B]().isInstanceOf[B] // true
In the situation described by #LuisMiguelMejíaSuárez in his comment
trait C extends A
you'll have
// test[C]() // doesn't compile, could not find implicit value for parameter makeT: MakeT[C]
Regarding generic return type see also
Why can't I return a concrete subtype of A if a generic subtype of A is declared as return parameter?
Type mismatch on abstract type used in pattern matching
Or you can use standard type classes =:=, <:<
def test[T <: A]()(implicit ev: B =:= T): T = {
new B {}
}
(not implicit ev: T =:= B)
or
def test[T <: A]()(implicit ev: B <:< T): T = {
new B {}
}

Returning a value from a method which must implement a typeclass

I am trying to have a method return a value that must implement a typeclass, and I think it is highlighting to me more generally how I don't understand Scala's generic parameter resolution process. I have a situation something like this:
trait IsContainer[A, T] {
def getOtherContainer[O](implicit tc2: IsUnit[O]): O
}
trait IsUnit[A] { }
implicit val anIntIsUnit = new IsUnit[Int] { }
implicit def aListIsContainer[T] = new IsContainer[List[T], T] {
def getOtherContainer[Int] = 3
}
This is raising a compile error: Missing implementation for: def getOtherContainer. My uninformed guess about what should be happening here is that Scala sees I have passed the generic parameter O, and considers the method implemented if all instances of the O type are consistent. So in this case (since I have explicitly told it that O = Int, it checks that there is an instance of IsUnit[Int] in scope, and that the output type of the method is of type O. If this is correct (and I'm not saying it is!) then why is this not working?
More generally, if I skipped the [O] generic parameter and let it guess - so I just implemented the method with getOtherContainer = 3 - should I also expect it work? To infer what O should be, does it scan over the line and see if O has been concretely filled out in any of the three places it is mentioned, and infer from that?
Thanks!
Correct is
implicit def aListIsContainer[T] = new IsContainer[List[T], T] {
override def getOtherContainer[O](implicit tc2: IsUnit[O]): O = ???
}
Your type class
trait IsContainer[A, T] {
def getOtherContainer[O](implicit tc2: IsUnit[O]): O
}
means that if a tuple of types A, T have an instance of the type class then you know how to do getOtherContainer for any type O having an instance of type class IsUnit.
When you're trying to remove (implicit tc2: IsUnit[O]) or [O] in the definition of an instance you're actually trying to violate the contract of type class.
If you want to specialize O in an instance (for example O := Int) then you should move type parameter O to the type class level
trait IsContainer[A, T, O] {
def getOtherContainer(implicit tc2: IsUnit[O]): O
}
or
abstract class IsContainer[A, T, O](implicit tc2: IsUnit[O]) {
def getOtherContainer: O
}
or
trait IsContainer[A, T] {
type O
def getOtherContainer(implicit tc2: IsUnit[O]): O
}
Then you can define an instance
implicit def aListIsContainer[T] = new IsContainer[List[T], T, Int] {
override def getOtherContainer(implicit tc2: IsUnit[Int]): Int = 3
}
or
implicit def aListIsContainer[T] = new IsContainer[List[T], T, Int] {
override def getOtherContainer: Int = 3
}
or
implicit def aListIsContainer[T] = new IsContainer[List[T], T] {
override type O = Int
override def getOtherContainer(implicit tc2: IsUnit[O]): O = 3
}
correspondingly.
The main problem is this definition:
def getOtherContainer[Int] = 3
In this definition, Int is a type parameter, not the type Int. It is exactly the same as
def getOtherContainer[T] = 3
So you have not "explicitly told it that O = Int", you have just used Int as the name of type parameter rather than O. Since this does not match the required signature in the trait, there is no valid implementation of getOtherContainer and you get the error.

Resolve implicit parameter from super type

Is it possible to resolve an implicit parameter for a type B if an implicit is defined for its super type A?
Here is an example :
I have an Enumerable typeclass :
trait Enumerable[A] {
def name(a: A): String
def list: List[A]
//... other methods
}
object Enumeration {
def name[A, T >: A](a: A)(implicit ev: Enumerable[T]) = ev.name(a)
def list[T](implicit ev: Enumerable[T]) = ev.list
// ...
}
Then I define an instance of enumerable :
sealed trait Season
case object Winter extends Season
case object Spring extends Season
case object Summer extends Season
case object Fall extends Season
implicit val seasonEnumerable = new Enumerable[Season] {
override def list: List[Season] = List(Winter, Spring, Summer, Fall)
}
// working :
Enumeration.name(Winter: Season) shouldBe "winter"
// faling :
Enumeration.name(Winter) shouldBe "winter"
Enumeration.name(Winter) is failing if I don't tell scalac that Winter is a Season. I've specified that the implicit parameter in the 'name' method signature is a supertype of A, but it's not sufficient...
Is there a better way to do this?
Eduardo's answer explains why the version with [A, T >: A] doesn't work. But there is a simpler solution to the problem than he gives: instead of introducing T infer as a type parameter, introduce it by an existential type:
def name[A](a: A)(implicit ev: Enumerable[T >: A] forSome { type T }) = ev.name(a)
Or, using a shorthand,
def name[A](a: A)(implicit ev: Enumerable[_ >: A]) = ev.name(a)
Then the compiler only has to decide what T is when looking for ev.
This is a common inconvenience whenever you need type-dependent types to be inferred. Your method
def name[A, T >: A](a: A)(implicit ev: Enumerable[T])
when called on Winter, first A will be inferred to Winter.type and then T to be A, as it conforms to that bound and there are no more constraints on it at that point. Then of course the compiler won't find an instance of Enumerable[Winter.type].
There's an easy solution with type members though:
trait AnyEnumerable {
type E
def name[A <: E](a: A): String
def list: List[E]
}
object Enumeration {
def name[A](a: A)(implicit ev: AnyEnumerable { type E >: A }) = ev.name(a)
def list[T](implicit ev: AnyEnumerable { type E = T }) = ev.list
// ...
}
// an implicit for `Season`
implicit val seasonEnumerable: AnyEnumerable { type E = Season } =
new AnyEnumerable {
type E = Season
def name[A <: Season](a: A): String = a.toString
def list: List[Season] = List(Winter, Spring, Summer, Fall)
}
// compiles!
val zzz = Enumeration.name(Winter)

Referring abstract type member of a type parameter

My scenario is like:
trait A {
type B
def foo(b: B)
}
trait C[D <: A] {
val d: D
def createB(): D#B
def bar() {
d.foo(createB)
}
}
In REPL, it complains
<console>:24: error: type mismatch;
found : D#B
required: C.this.d.B
a.bar(createB())
What's wrong with this ? And (if possible at all) how to correct this code ?
D#B is a type projection, and is not the same as d.B. You have a type mismatch because in foo, Bactually meant this.B, which as said is not the same as D#B (the latter being more general).
Informally, you can think of D#Bas representing any possible type that the abstract type B can take for any instance of D, while d.B is the type of B for the specific instance d.
See What does the `#` operator mean in Scala? and What is meant by Scala's path-dependent types? for some context.
One way to make it compile it is by changing createB's return type to d.B:
def createB(): d.B
However in many cases such a solution is too restrictive because you are tied to the specific instance d, which might not be what you had in mind.
Another solution is then to replace the abstract type with a type parameter (though it is more verbose):
trait A[B] {
def foo(b: B)
}
trait C[B, D <: A[B]] {
val d: D
def createB(): B
def bar() {
d.foo(createB)
}
}
Update given this answer I'm not sure whether this should be considered a bug or not
This is a bug: SI-4377. An explicit type ascription yields
trait C[D <: A] {
val d: D
def createB(): D#B
def bar() {
(d:D).foo(createB)
// [error] found : D#B
// [error] required: _3.B where val _3: D
}
}
which looks like the implementation leaking. There's a workaround which involves casting to an intersection type (dangerous, casting is wrong etc; see my other answer here)
trait A {
type B
def foo(b: B)
}
case object A {
type is[A0 <: A] = A0 {
type B = A0#B
}
def is[A0 <: A](a: A0): is[A0] = a.asInstanceOf[is[A0]]
}
trait C[D <: A] {
val d: D
def createB(): D#B
def bar() {
A.is(d).foo(createB) // use it here!
}
}

Constraining an operation by matching a type parameter to an argument's path-dependent type

I would like to exploit Scala's type system to constrain operations in a system where there are versioned references to some values. This is all happening in some transactional context Ctx which has a version type V attached to it. Now there is a Factory to create reference variables. They get created with a creation version attached them (type parameter V1), corresponding to the version of the context in which the factory was called.
Now imagine that some code tries to access that reference in a later version, that is using a different Ctx. What I want to achieve is that it is prohibited to call access on that Ref in any version (Ctx's V type field) that doesn't match the creation version, but that you are allowed to resolve the reference by some substitution mechanism that returns a new view of the Ref which can be accessed in the current version. (it's ok if substitute is called with an invalid context, e.g. one that is older than the Ref's V1 -- in that case a runtime exception could be thrown)
Here is my attempt:
trait Version
trait Ctx {
type V <: Version
}
object Ref {
implicit def access[C <: Ctx, R, T](r: R)(implicit c: C, view: R => Ref[C#V, T]): T =
view(r).access(c)
implicit def substitute[C <: Ctx, T](r: Ref[_ <: Version, T])
(implicit c: C): Ref[C#V, T] = r.substitute(c)
}
trait Ref[V1 <: Version, T] {
def access(implicit c: { type V = V1 }): T // ???
def substitute[C <: Ctx](implicit c: C): Ref[C#V, T]
}
trait Factory {
def makeRef[C <: Ctx, T](init: T)(implicit c: C): Ref[C#V, T]
}
And the problem is to define class method access in a way that the whole thing compiles, i.e. the compound object's access should compile, but at the same time that I cannot call this class method access with any Ctx, only with one whose version matches the reference's version.
Preferably without structural typing or anything that imposes performance issues.
FYI, and to close the question, here is another idea that I like because the client code is fairly clutter free:
trait System[A <: Access[_]] {
def in[T](v: Version)(fun: A => T): T
}
trait Access[Repr] {
def version: Version
def meld[R[_]](v: Version)(fun: Repr => Ref[_, R]): R[this.type]
}
trait Version
trait Ref[A, Repr[_]] {
def sub[B](b: B): Repr[B]
}
object MyRef {
def apply[A <: MyAccess](implicit a: A): MyRef[A] = new Impl[A](a)
private class Impl[A](a: A) extends MyRef[A] {
def sub[B](b: B) = new Impl[B](b)
def schnuppi(implicit ev: A <:< MyAccess) = a.gagaism
}
}
trait MyRef[A] extends Ref[A, MyRef] {
// this is how we get MyAccess specific functionality
// in here without getting trapped in more type parameters
// in all the traits
def schnuppi(implicit ev: A <:< MyAccess): Int
}
trait MyAccess extends Access[MyAccess] {
var head: MyRef[this.type]
var tail: MyRef[this.type]
def gagaism: Int
}
def test(sys: System[MyAccess], v0: Version, v1: Version): Unit = {
val v2 = sys.in(v0) { a => a.tail = a.meld(v1)(_.head); a.version }
val a3 = sys.in(v2) { a => a }
val (v4, a4) = sys.in(v1) { a =>
a.head = a.head
println(a.head.schnuppi) // yes!
(a.version, a)
}
// a3.head = a4.head // forbidden
}
The following seems to work:
trait Version
trait Ctx[+V1 <: Version] {
type V = V1
}
type AnyCtx = Ctx[_ <: Version]
type AnyRf[T] = Ref[_ <: Version, T]
object Ref {
implicit def access[C <: AnyCtx, R, T](r: R)(
implicit c: C, view: R => Ref[C#V, T]): T = view(r).access(c)
implicit def substitute[C <: AnyCtx, T](r: AnyRf[T])(implicit c: C): Ref[C#V, T] =
r.substitute( c )
}
trait Ref[V1 <: Version, T] {
def access(implicit c: Ctx[V1]): T
def substitute[C <: AnyCtx](implicit c: C): Ref[C#V, T]
}
trait Factory {
def makeVar[C <: AnyCtx, T](init: T)(implicit c: C): Ref[C#V, T]
}
// def shouldCompile1(r: AnyRf[String])(implicit c: AnyCtx): String = r
def shouldCompile2(r: AnyRf[String])(implicit c: AnyCtx): String = {
val r1 = Ref.substitute(r)
r1.access(c)
}
// def shouldFail(r: AnyRf[String])(implicit c: AnyCtx): String = r.access(c)
So the follow-up questions are
why I need a redundancy of the type
parameter for Ctx to achieve this. I hate that these type
parameters accumulate like rabbits in my code.
why shouldCompile1 doesn't compile
—can i get the implicits to work as planned?
EDIT:
This is wrong, too. The variance annotation is wrong. Because now the following compiles although it shouldn't:
def versionStep(c: AnyCtx): AnyCtx = c // no importa
def shouldFail3[C <: AnyCtx](f: Factory, c: C): String = {
val r = f.makeVar("Hallo")(c)
val c2 = versionStep(c)
r.access(c2)
}