Take a look at this:
/** takes a Spellbook and returns a Spellbook guaranteeing
* that all spells have been loaded from the database. */
def checkIfSpellsLoaded[S <: Spellbook](spellbook :S) :Option[S { type SpellsLoaded }] =
if (spellbook.spellsLoaded) Some(spellbook.asInstanceOf[S { type SpellsLoaded }])
else None
def checkIfOwnerLoaded[S <: Spellbook](spellbook :S) :Option[S { type OwnerLoaded }] =
if (spellbook.ownerLoaded) Some(spellbook.asInstanceOf[S { type OwnerLoaded }])
else None
What is that { type X } doing as part of a type parameter?? What is going on here?
In Scala class members can be def, val and (relevant for us) type
https://docs.scala-lang.org/tour/abstract-type-members.html
https://typelevel.org/blog/2015/07/13/type-members-parameters.html
Scala: Abstract types vs generics
How to work with abstract type members in Scala
Type members are used to create path-dependent types
What is meant by Scala's path-dependent types?
https://docs.scala-lang.org/scala3/book/types-dependent-function.html
If Spellbook has type members SpellsLoaded, OwnerLoaded
trait Spellbook {
type SpellsLoaded
type OwnerLoaded
def spellsLoaded: Boolean
def ownerLoaded: Boolean
}
then for S <: Spellbook the types S, S { type SpellsLoaded } and S { type OwnerLoaded } are the same
type S <: Spellbook
implicitly[(S { type SpellsLoaded }) =:= S] // compiles
implicitly[S =:= (S { type SpellsLoaded })] // compiles
implicitly[(S { type OwnerLoaded }) =:= S] // compiles
implicitly[S =:= (S { type OwnerLoaded })] // compiles
But if Spellbook doesn't have type members SpellsLoaded, OwnerLoaded
trait Spellbook {
// no SpellsLoaded, OwnerLoaded
def spellsLoaded: Boolean
def ownerLoaded: Boolean
}
then the refined types S { type SpellsLoaded } and S { type OwnerLoaded } are just subtypes of S (having those type members)
implicitly[(S { type SpellsLoaded }) <:< S] // compiles
// implicitly[S <:< (S { type SpellsLoaded })] // doesn't compile
implicitly[(S { type OwnerLoaded }) <:< S] // compiles
// implicitly[S <:< (S { type OwnerLoaded })] // doesn't compile
and the refined types S { type SpellsLoaded = ... } and S { type OwnerLoaded = ... } in their turn are subtypes of the former refined types
implicitly[(S {type SpellsLoaded = String}) <:< (S {type SpellsLoaded})] // compiles
// implicitly[(S {type SpellsLoaded}) <:< (S {type SpellsLoaded = String})] // doesn't compile
implicitly[(S {type OwnerLoaded = Int}) <:< (S {type OwnerLoaded})] // compiles
// implicitly[(S {type OwnerLoaded}) <:< (S {type OwnerLoaded = Int})] // doesn't compile
S { type SpellsLoaded } and S { type OwnerLoaded } are shorthands for S { type SpellsLoaded >: Nothing <: Any } and S { type OwnerLoaded >: Nothing <: Any } while S { type SpellsLoaded = SL } and S { type OwnerLoaded = OL } are shorthands for S { type SpellsLoaded >: SL <: SL } and S { type OwnerLoaded >: OL <: OL }.
Casting .asInstanceOf[S { type SpellsLoaded }], .asInstanceOf[S { type OwnerLoaded }] looks like SpellsLoaded, OwnerLoaded are used as phantom types
https://books.underscore.io/shapeless-guide/shapeless-guide.html#sec:labelled-generic:type-tagging (5.2 Type tagging and phantom types)
So you seem to encode in types that the methods checkIfSpellsLoaded, checkIfOwnerLoaded were applied to S.
See also
Confusion about type refinement syntax
What is a difference between refinement type and anonymous subclass in Scala 3?
Related
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 {}
}
Problem:
trait UpperBound[O]
trait High[F[O] <: UpperBound[O]]
def canEqual(that :Any) = that.isInstanceOf[High[_]]
def high(h :High[_]) = ???
Does not compile, because scalac sees the _ type instead of a type constructor it expects. How to fix it, ideally without writing a novel?
Original question (before edits in reply to Dmytro's answer) had:
def canEqual(that :Any) = that.isInstanceOf[High[m forSome { type m[O] <: UpperBound[O] }]]
def high(h :High[m forSome { type m[O] <: UpperBound[O] }] = ???
Is there a shorter way of writing the above two methods by using some wildcard expression?
Simply using _ in High's type parameter position doesn't work as the kind doesn't match, and _[_] is not even a valid type expression.
If you make existential quantization outside High then it's just
type T = High[F] forSome { type F[O] <: UpperBound[O] }
def canEqual(that: Any) = that.isInstanceOf[T]
def high(h: T) = ???
If you make existential quantization inside High then since
implicitly[(n forSome { type n <: Upper}) =:= Upper]
implicitly[(m[O1] forSome { type m[O] <: UpperBound[O]}) =:= UpperBound[O1]]
(and vice versa) it's just High[UpperBound]
implicitly[High[m forSome { type m[O] <: UpperBound[O] }] =:= High[UpperBound]]
def canEqual(that: Any) = that.isInstanceOf[High[UpperBound]]
def high(h: High[UpperBound]) = ???
An existential type π forSome { π } where π contains a clause type π‘[tps]>:πΏ<:π is equivalent to the type πβ² forSome { π } where πβ² results from π by replacing every covariant occurrence of π‘ in π by π and by replacing every contravariant occurrence of π‘ in π by πΏ.
https://scala-lang.org/files/archive/spec/2.13/03-types.html#simplification-rules
I've been mulling over a design problem in a library I'm working on, and I realized that using existential types may allow me to change my design in a way that simplifies many parts of my library. However, I can't quite seem to get it to work.
It seems to me that myBuilder conforms to the type MultiSignalBuilder[E, R] forSome { type E[+X] >: Element[X] }, where Element[X] is MultiSignalElement[X], but the compiler says it does't. It seems to have to do the fact that E is a higher-kinded type. Why doesn't this work, and is there a way to fix it?
class MultiSignalElement[+T] {
}
abstract class MultiSignal[+T] {
type Element[+X] <: MultiSignalElement[X]
val element : Element[T]
def transform[R[+X] <: MultiSignal[X]](builder : MultiSignalBuilder[E, R] forSome { type E[+X] >: Element[X] }) : R[T] =
builder.buildNew(element)
}
abstract class MultiSignalBuilder[-E[+X] <: MultiSignalElement[X], +R[+X] <: MultiSignal[X]] {
def buildNew[T](element : E[T]) : R[T]
}
object myBuilder extends MultiSignalBuilder[MultiSignalElement, MultiSignal] {
def buildNew[T](e : MultiSignalElement[T]) = new MultiSignal[T]() {
type Element[+X] = MultiSignalElement[X]
val element = e
}
}
val multiSignal = new MultiSignal[Int] {
type Element[+X] = MultiSignalElement[X]
val element = new MultiSignalElement()
}
multiSignal.transform(myBuilder) //type error on this line
multiSignal.transform[MultiSignal](myBuilder) //type error on this line
Let do step-by-step analysis.
First we have
def transform[R](builder : MultiSignalBuilder[E, R] forSome { type E[+X] >: Element[X] }) : Unit = { }
Which is equivalent to statement : there exists
type E[+X] >: Element[X]
For which we can define
def transform[E[+X] >: Element[X], R[+_]](builder : MultiSignalBuilder[E, R] ) : Unit = { }
Here we have an error
Error:(7, 18) covariant type X occurs in contravariant position in
type [+X] >: MultiSignal.this.Element[X] of type E
This is something. You are expecting your mysterious existential covariant type should be a supertype of another covariant type. I think this is the first thing which is freaking the compiler. Lets change relation to subtyping
def transform[E[+X] <: Element[X], R[+_]](builder : MultiSignalBuilder[E, R] ) : Unit = { }
Now we have
Error:(7, 56) type arguments [E,R] do not conform to class
MultiSignalBuilder's type parameter bounds [-E[+X] <:
MultiSignalElement[X],+R[+X] <: MultiSignal[X]]
So we forgot to require subtyping of MultiSignal[X] out of R parameter.
Lets change it
def transform[E[+X] <: Element[X], R[+X] <: MultiSignal[X]](builder : MultiSignalBuilder[E, R] ) : Unit = { }
Now
multiSignal.transform[MultiSignalElement,MultiSignal](myBuilder)
Is succesfully compiled.
Finally we could get back to existential version
def transform[R[+X] <: MultiSignal[X]](builder : MultiSignalBuilder[E, R] forSome {type E[+X] <: Element[X]}) : Unit = { }
With which
multiSignal.transform[MultiSignal](myBuilder)
Is succesfully compiled.
Sadly
multiSignal.transform(myBuilder)
Still is not compiled. I think there is too much type relations to resolve for the compiler.
The following piece of code does not compile :
trait A[F] {
def find(x: Int): F
def fill(f: F): Unit
}
object TestA {
def test[T <: A[F] forSome { type F }](t: T) =
t.fill(t.find(0))
}
It returns the following compilation error :
test.scala:8: error: type mismatch;
found : (some other)F(in type T)
required: F(in type T)
t.fill(t.find(0))
^
However the following code complies just fine :
trait B[F] {
type R = F
def find(x: Int): R
def fill(f: R): Unit
}
object TestB {
def test[T <: B[F] forSome { type F }](t: T) =
t.fill(t.find(0))
}
I have two questions here :
I expect the fist piece of code to compile. Why does it not?
If there is a good reason why first piece of code does not compile, I would expect the second to not compile either, for the same reason. How then, does it compile successfully?
Is either of these a bug?
I don't know why the compiler differentiates the two pieces of code. Basically, the code doesn't compile because the type returned by find and the type expected by fill don't have to be the same F, at least if find and fill were called on two different objects.
You could make the first piece of code to compile with:
def test[T <: A[F], F](t: T) =
t.fill(t.find(0))
And you could make the second piece of code not to compile with:
def test[T <: B[F] forSome { type F }](t: T, u: T) =
t.fill(u.find(0))
This should be rather a comment than an answer, but I don't have 50 reputation yet.
To understand what's happening, let's look at simpler versions of TestA.test and TestB.test.
object TestA {
def test1(a: A[String]) = {
val s: String = a.find(0)
a.fill(s)
}
}
object TestB {
def test1(b: B[String]) = {
val r: b.R = b.find(0)
b.fill(r)
}
}
Notice how the type of the intermediate value s refers to String, while the type of the intermediate value r does not.
object TestA {
def test2(a: A[F] forSome {type F}) = {
val s: F forSome {type F} = a.find(0)
// type mismatch;
// found : s.type (with underlying type F forSome { type F })
// required: F
a.fill(s)
}
}
object TestB {
def test2(b: B[F] forSome {type F}) = {
val r: b.R = b.find(0)
b.fill(r)
}
}
Once we throw in the existentials, we end up with an intermediate value s whose type is equivalent to Any, and which thus isn't a valid input for a.fill. The intermediate type for r, however, is still b.R, and so it is still an appropriate input for b.fill. The reason its type is still b.R is because b.R doesn't refer to F, and so according to the simplification rules for existential types, b.R forSome {type F} is equivalent to b.R, in the same way that Int forSome {type F} is equivalent to Int.
Is either of these a bug?
Well, there is certainly a bug somewhere (as of scalac 2.11.7), because the following does not type check.
object TestB {
def test3(b: B[F] forSome {type F}) = {
val r: b.R forSome {type F} = b.find(0)
// type mismatch;
// found : F
// required: b.R
// (which expands to) F
b.fill(r)
}
}
So either I'm wrong to think that b.R does not refer to F, in which case b.R forSome {type F} is not equivalent to b.R and your TestB.test should not type check but it does, or b.R forSome {type F} is equivalalent to b.R, in which case my TestB.test3 should type check but it doesn't.
I'm quite convinced that the bug is with the latter, because the error even occurs when the existential quantification has nothing to do with b.R, as in the following example.
object TestB {
def test4(b: B[F] forSome {type F}) = {
val r: b.R forSome {val x: Int} = b.find(0)
// type mismatch;
// found : F
// required: b.R
// (which expands to) F
b.fill(r)
}
}
Ok, so I have this wonderfully simple setup:
trait Sys[S <: Sys[S]]
trait Elem[S <: Sys[S]]
trait AttrElem[S <: Sys[S]] {
type E <: Elem[S]
def attributes: Any
def element: E
}
And a factory:
object Factory {
def apply[S <: Sys[S], E1 <: Elem[S]](
elem: E1): AttrElem[S] { type E = E1 } = new Impl(elem)
private class Impl[S <: Sys[S], E1 <: Elem[S]](val element: E1)
extends AttrElem[S] {
type E = E1
def attributes = 1234
}
}
Now in practice the f*** Scala type inference breaks down:
def test[S <: Sys[S]](elem: Elem[S]): Unit = {
Factory(elem)
}
<console>:62: error: inferred type arguments [Nothing,Elem[S]] do not conform
to method apply's type parameter bounds [S <: Sys[S],E1 <: Elem[S]]
Factory(elem)
^
So my next attempt is existential types:
object Factory {
def apply[S <: Sys[S], E1[~] <: Elem[~] forSome { type ~ <: Sys[~] }](
elem: E1[S]): AttrElem[S] { type E = E1[S] } = new Impl(elem)
private class Impl[S <: Sys[S], E1[~] <: Elem[~] forSome { type ~ <: Sys[~] }](
val element: E1[S]) extends AttrElem[S] {
type E = E1[S]
def attributes = 1234
}
}
This gives me this following lovely message:
<console>:62: error: inferred kinds of the type arguments (S,E1[S]) do not
conform to the expected kinds of the type parameters (type S,type E1) in
class Impl.
E1[S]'s type parameters do not match type E1's expected parameters:
type E1 has one type parameter, but type E1 (in class Impl) has one
elem: E1[S]): AttrElem[S] { type E = E1[S] } = new Impl(elem)
^
"type E1 has one type parameter, but type E1 has one" -- huh?
Question: How can I define the factory's apply method to infer the types?
The following "redundancy" seems to satisfy the compiler:
def apply[S <: Sys[S], E1 <: Elem[S]](elem: E1 with Elem[S]):
AttrElem[S] { type E = E1 } = ...
That is, adding with Elem[S]. It looks like a needless deficiency of the Scala compiler to not infer S from E1 <: Elem[S] where Elem is invariant in S.
Or am I missing a crucial bit?