I am struggling with dependent types in Scala 2.11.7. Here is the context:
trait Counter {
type T
def zero: T
def incr( t: T ): T
}
object IntCounter extends Counter {
type T = Int
val zero = 0
def incr( t: Int ) = t + 1
}
case class Foo( counter: Counter )
def twice( foo: Foo )( cntr: foo.counter.T ): foo.counter.T =
foo.counter.incr( foo.counter.incr( cntr ) )
So far so good, everything compiles. However I would like to add an object which contains both a Foo instance and a corresponding counter state. For instance:
trait Bar {
val foo: Foo
val current: foo.counter.T
}
The definition is OK (provided I use abstract vals). But I cannot define a factory method (aka smart constructors). All my naive attempts fail to compile. For example, the definition:
def bar( f: Foo )( cntr: f.counter.T ): Bar = new Bar {
val foo = f
val current = cntr
}
fails to compile with the error:
xxx: overriding value current in trait Bar of type this.foo.counter.T;
value current has incompatible type
val current = cntr
^
How can I force the compiler to understand that both type are indeed the same ? I could solve the problem with generics instead but I prefer to avoid this option if possible.
If bar is single constructor for Bar, you could solve it like this:
sealed trait Foo { //can't be case class because you can't even call `twice` method then
type Ctr <: Counter
type Z <: Ctr#T
val counter: Ctr
}
def foo[Ct <: Counter](ctr: Ct): Foo{type Ctr = Ct} = new Foo {
type Ctr = Ct
type Z = ctr.T
val counter = ctr
}
sealed trait Bar {
type Ctrr <: Counter
type TT <: Counter#T
val foo: Foo {type Ctr = Ctrr}
val current: TT
}
def bar[Ct <: Counter]( f: Foo{type Ctr = Ct} )( cntr: f.counter.T )(implicit ev: Ct =:= f.Ctr): Bar {type Ctrr = Ct; type TT = f.counter.T} = new Bar {
type Ctrr = Ct
type TT = f.counter.T
val foo = f
val current = cntr
}
Usage:
scala> val br = bar(foo(IntCounter))(5)
br: Bar{type Ctrr = IntCounter.type; type TT = Int} = $anon$1#35267fd4
scala> br.foo.counter.incr(br.current)
res41: Int = 6
The disadvantage here is that you have to specify (and maintain) the same root-type between TT and foo members wherever you create a new Foo instance.
Related
class S {
case class A(a: Int)
}
abstract class R(val s: S) {
type T1 = R.this.s.A
type T2 = s.A
implicitly[T1 =:= T2] // compiles
type T3 = R.this.type
type T4 = this.type
implicitly[T3 =:= T4] // compiles
val v1 = R.this // v1 == `this`
val v2 = R.this.s // v2 == `s`
}
Looks like the .this part has no effect whatsoever. To make a concrete question:
When do you use the .this ?
It matters for inner classes. E.g.
class Outer(val x: Int) {
class Inner(val x: Int) {
def outer_this = Outer.this.x
def inner_this = this.x // or just x
}
}
val outer = new Outer(0)
val inner = new outer.Inner(1)
println(inner.outer_this) // 0
println(inner.inner_this) // 1
Each Outer.Inner instances "belongs to" a specific Outer instance, and can refer to that instance as Outer.this. By itself x in Inner refers to its own property, so if you need the enclosing instance's x property, you write Outer.this.x.
One rule is Scala never infers singleton type this.type. For example first consider the mechanics of it
scala> trait Foo {
| type T
| def f() = this // we left out the return type to see what Scala will infer
| }
// defined trait Foo
scala> new Foo { type T = String }
val res0: Foo{T = String} = anon$1#6d3ad37a
scala> res0.f()
val res1: Foo = anon$1#6d3ad37a
Note how res1 has return type of Foo and not Foo { type T = String }, so we have lost some type information
scala> val x: res1.T = ""
1 |val x: res1.T = ""
| ^^
| Found: ("" : String)
| Required: res1.T
Note compiler does not know res1.T is actually a String. So compiler did not infer the singleton type this.type which would have all the type information including what type member T was instantiated to
scala> trait Foo {
| type T
| def f(): this.type = this
| }
// defined trait Foo
scala> new Foo { type T = String }
val res2: Foo{T = String} = anon$1#7d381eae
scala> res2.f()
val res3: Foo{T = String} = anon$1#7d381eae
scala> val x: res3.T = ""
val x: res3.T = ""
Note how after we explicitly declared singleton return type this.type compiler knows T is a String.
Here is another mechanical example of what happens because compiler does not infer singleton type this.type
scala> trait Foo {
| def f() = this // let inference do its thing
| }
// defined trait Foo
scala> trait Bar {
| def g() = 42
| }
// defined trait Bar
scala> trait Bar extends Foo {
| def g(): Int = 42
| }
// defined trait Bar
scala> new Bar {}
val res5: Bar = anon$1#6a9a6a0c
scala> res5.f()
val res6: Foo = anon$1#6a9a6a0c
scala> res6.g()
1 |res6.g()
|^^^^^^
|value g is not a member of Foo
Note how f() call typed to Foo and not perhaps expected Bar. On the other hand if we provide explicit singleton return type this.type then
scala> trait Foo {
| def f(): this.type = this
| }
// defined trait Foo
scala> trait Bar extends Foo {
| def g(): Int = 42
| }
// defined trait Bar
scala> new Bar {}
val res7: Bar = anon$1#4707d60a
scala> res7.f()
val res8: Bar = anon$1#4707d60a
scala> res8.g()
val res9: Int = 42
we see f() call typed to Bar.
Those were the mechanics but what about practical applications? Two uses I am aware of are:
supporting fluent style of programming
supporting summoners of type class instances (see also Scala 3 summon)
Why does the following type equivalence hold
trait Foo { type T }
val fa = new Foo { type T = Int }
implicitly[fa.T =:= Int] // OK
but when type member T is assigned via method parameter A then type equivalence does not hold
def makeFoo[A]: Foo = new Foo { type T = A }
val fb = makeFoo[Int]
implicitly[fb.T =:= Int] // ERROR
Intuitively I would expect if T = A and A = Int then T = Int?
This is because the return type of makeFoo is only Foo and not Foo { type T = A }. If you don't explicitly declare it or if you refine it, the error will go away.
def makeFoo[A] = new Foo { type T = A }
val fb = makeFoo[Int]
implicitly[fb.T =:= Int] // No more error
or
def makeFoo2[A]: Foo { type T = A } = new Foo { type T = A }
val fc: Foo { type T = Int } = makeFoo[Int]
implicitly[fc.T =:= Int] // No more error
Scastie:
<script src="https://scastie.scala-lang.org/knIfPcXqSQKXXCXJ2teHkg.js"></script>
I'll just add that possibility to specify not the precise return type but its supertype (an upper bound) is called whiteboxity.
In Scala 2 this can be achieved with whitebox macros
import scala.language.experimental.macros
import scala.reflect.macros.whitebox
def makeFoo[A]: Foo = macro impl[A]
def impl[A: c.WeakTypeTag](c: whitebox.Context): c.Tree = {
import c.universe._
q"new Foo { type T = ${weakTypeOf[A]} }"
}
val fb = makeFoo[Int]
implicitly[fb.T =:= Int] //compiles
In Dotty this can be achieved with keyword transparent (currently)
transparent inline def makeFoo[A]: Foo = new Foo { type T = A }
val fb = makeFoo[Int]
summon[fb.T =:= Int] //compiles
Former syntax is
inline def makeFoo[A] <: Foo = new Foo { type T = A }
I’m having some problems with path dependent types.
I have some types Foo with an abstract type member F. Instances such as Bar will provide the concrete type.
Then there is a type class Baz. I have instances of the type class for each concrete type of Foo#F (but not for Foo itself).
Here is an example:
sealed trait Foo {
type F
}
object Bar extends Foo {
type F = Array[Byte]
}
trait Baz[B] {
def b(b: B): String
}
object Baz {
implicit val bazByteArray: Baz[Array[Byte]] = (b: Array[Byte]) => new String(b)
}
I can't get this to compile:
def f(a: Foo): Baz[a.F] = {
val baz = a match {
case bar#Bar => g(bar)
}
baz
} // Expression of type Baz[(a.type with Bar.type)#F] doesn't conform to Baz[a.F]
val x2: Foo = Bar
val y2: Baz[x2.F] = f(x2) // Expression of type Baz[Foo#F] doesn't conform to expected type Baz[x2.F]
This does compile:
def g(a: Foo)(implicit baz: Baz[a.F]): Baz[a.F] = {
baz
}
val x1: Bar.type = Bar
val y1: Baz[x1.F] = f(x1)
Why does g compile but not f? Aren't the types the same?
How can I get f to compile? Is there some sort of evidence I need to add?
Seems somewhat similar to this question. Here is a way to make it compile:
sealed trait Foo {
type F
def asSingleton: FooSingleton[F]
}
trait FooSingleton[X] extends Foo {
type F = X
def asSingleton: FooSingleton[X] = this
}
object Bar extends FooSingleton[Array[Byte]]
trait Baz[B] {
def b(b: B): String
}
object Baz {
implicit val bazByteArray: Baz[Array[Byte]] =
(b: Array[Byte]) => new String(b)
}
def g(a: Foo)(implicit baz: Baz[a.F]): Baz[a.F] = {
baz
}
val x1: Bar.type = Bar
val y1: Baz[x1.F] = f(x1)
def f[T](a: Foo { type F = T } ): Baz[T] = {
(a.asSingleton: FooSingleton[T]) match {
case bar # Bar => g(bar)
}
}
val x2: Foo = Bar
val y2: Baz[x2.F] = f(x2)
Your g compiles, because the path-dependent argument baz of type Baz[a.F] comes from the outside, a concrete implicit instance is inserted by the compiler, and the actual value a isn't used anywhere inside g.
Your f does not compile, because the B[a.F] appears only in the return type, and it cannot be made more concrete before an actual argument a is passed to f.
In a sense, the f breaks the path between the argument a and the returned value, because it makes the following "discontinuous jumps":
Start with a: Foo
Jump from a to the Bar singleton (by pattern-matching)
Use g to get from the concrete Bar singleton to the concrete Baz[Array[Byte]]
try to return this Baz[Array[Byte]], which seems no longer connected to Baz[a.F].
This path can be repaired by proving that the discontinuous "jump" is indeed just an identity path that stays in the same spot all the time, so it really doesn't move anywhere, so that a.F and the inferred type are the same, namely T.
Ideally I'd like to write Haskell style pattern matching on the type level in Scala, something like this:
Can shapeless be used for something like this ?
object Test{
type F[Int] = String
type F[Boolean] = Int // but this line does not compile
implicitly[String =:= F[Int]]
implicitly[Int =:= F[Boolean]]
}
In this example if F takes Int then it returns String and if it takes Boolean then it returns Int.
Clarification (based on this answer)
Here is how I'd like to use these types in functions and typeclasses:
abstract class WrappedF[T] {
type F
type Unwrap = T
}
type F[X <: WrappedF[_]] = X#F
class IntF extends WrappedF[Int] { type F = StringF }
class BooleanF extends WrappedF[Boolean] { type F = IntF }
class StringF extends WrappedF[String] { type F = Nothing }
implicitly[String =:= F[IntF]#Unwrap]
implicitly[Int =:= F[BooleanF]#Unwrap]
implicitly[String =:= F[F[BooleanF]]#Unwrap]
// this is a type class definition where `V` is a member of the `Test` class
// `f`'s type should be defined by `V`, but it does not work :(
trait Test[V <: WrappedF[V]]{
def f(a: F[V]#Unwrap): F[V]#Unwrap // this does not compile
}
implicit object TestImpl extends Test[IntF]{
override def f(a: F[IntF]#Unwrap): F[IntF]#Unwrap = {
val z: F[IntF]#Unwrap = "fd"+a
z
}
}
Here two non-shapeless solutions.
A little type-level table of constants with funny names:
type F = {
type Int = java.lang.String
type Boolean = scala.Int
}
implicitly[String =:= F#Int]
implicitly[Int =:= F#Boolean]
Here, F is a type with two type members that have names Int and Boolean (could be I and B, those two constants aren't really connected with Int or Boolean in any way). This doesn't compose: you can't write down something like F#F#Int.
You can lift every type T for which you want to define F into a type that has both T and F[T] as type members:
abstract class WrappedF[T] {
type F
type Unwrap = T
}
type F[X <: WrappedF[_]] = X#F
class IntF extends WrappedF[Int] { type F = StringF }
class BooleanF extends WrappedF[Boolean] { type F = IntF }
class StringF extends WrappedF[String] { type F = Nothing }
implicitly[String =:= F[IntF]#Unwrap]
implicitly[Int =:= F[BooleanF]#Unwrap]
implicitly[String =:= F[F[BooleanF]]#Unwrap]
This adds more noise because of ...F and #Unwrap, but is purely a type-level computation, and it composes (as the last example shows).
Update (better suited for abstracting over V <: WrappedF)
In your code under "Clarification", you were missing F <: WrappedF bound on the type member F:
abstract class WrappedF {
type F <: WrappedF
type Unwrap
}
type F[X <: WrappedF] = X#F
class IntF extends WrappedF { type Unwrap = Int; type F = StringF }
class BooleanF extends WrappedF { type Unwrap = Boolean; type F = IntF }
class StringF extends WrappedF { type Unwrap = String; type F = Nothing }
implicitly[String =:= F[IntF]#Unwrap]
implicitly[Int =:= F[BooleanF]#Unwrap]
implicitly[String =:= F[F[BooleanF]]#Unwrap]
trait Test[V <: WrappedF]{
def f(a: F[V]#Unwrap): F[V]#Unwrap // this does not compile
}
implicit object TestImpl extends Test[IntF] {
override def f(a: String): String = {
val z: F[IntF]#Unwrap = "fd" + a
z
}
}
You use a typeclass style implicit structure with a type member.
// sealed for closed family
class F[I] { type O }
object F {
type Rel[I, O0] = F[I] { type O = O0 }
/* private */ def mkF[I, O0]: Rel[I, O0] = new F[I] { override type O = O0 }
implicit val fInt: Rel[Int, String] = mkF
implicit val fBoolean: Rel[Boolean, Int] = mkF
def apply[I](implicit f: F[I]): f.type = f // f.type survives the refinement (see what breaks on removal)
}
locally { val temp = F[Int]; implicitly[temp.O =:= String] }
locally { val temp = F[Boolean]; implicitly[temp.O =:= Int] }
locally { val temp0 = F[Boolean]; val temp1 = F[temp0.O]; implicitly[temp1.O =:= String] }
EDIT 2:
I managed to achieve the type safety I wanted in my exercise with RomanNumerals using a combination of mixins and type parameters with the code below. In essence what it does is after importing everything in RomanNumerals I am able to write L X V I, but not L L or X L X cause then I get a type mismatch compiler error (since those would be illegal combinations of roman numerals). Now I'm wondering if this way of using traits, mixins and type parameters is considered ok or if I'm abusing the language so to speak :) Is there a better way of achieving the same kind of type safety with some simpler/cleaner code?
object RomanNumerals {
trait Numeral {
def num:Int
}
trait NumeralI[N<:Numeral] extends Numeral
trait NumeralV[N<:Numeral] extends NumeralI[N] {
def I = new RomanNumeral[Numeral](1 + this.num) with Numeral
def II = new RomanNumeral[Numeral](2 + this.num) with Numeral
def III = new RomanNumeral[Numeral](3 + this.num) with Numeral
}
trait NumeralX[N<:Numeral] extends NumeralV[N] {
def IV = new RomanNumeral[Numeral](4
+ this.num) with Numeral
def V = new RomanNumeral[NumeralI[Numeral]](5
+ this.num) with NumeralV[NumeralI[Numeral]]
def IX = new RomanNumeral[Numeral](9
+ this.num) with Numeral
}
trait NumeralL[N<:Numeral] extends NumeralX[N] {
def X = new RomanNumeral[NumeralV[Numeral]](10
+ this.num) with NumeralX[NumeralV[Numeral]]
def XX = new RomanNumeral[NumeralV[Numeral]](20
+ this.num) with NumeralX[NumeralV[Numeral]]
def XXX = new RomanNumeral[NumeralV[Numeral]](30
+ this.num) with NumeralX[NumeralV[Numeral]]
}
class RomanNumeral[T <: Numeral](val num:Int) {
override def toString = num toString
def apply[N >: T <: Numeral](rn:NumeralI[N]) =
new RomanNumeral[Numeral](rn.num + num) with Numeral
def apply[N >: T <: Numeral](rn:NumeralV[N]) =
new RomanNumeral[NumeralI[Numeral]](rn.num
+ num) with NumeralV[NumeralI[Numeral]]
def apply[N >: T <: Numeral](rn:NumeralX[N]) =
new RomanNumeral[NumeralV[Numeral]](rn.num
+ num) with NumeralX[NumeralV[Numeral]]
def apply[N >: T <: Numeral](rn:NumeralL[N]) =
new RomanNumeral[NumeralX[Numeral]](rn.num
+ num) with NumeralL[NumeralX[Numeral]]
}
val I = new RomanNumeral[NumeralI[Numeral]](1) with NumeralI[Numeral]
val II = new RomanNumeral[NumeralI[Numeral]](2) with NumeralI[Numeral]
val III = new RomanNumeral[NumeralI[Numeral]](3) with NumeralI[Numeral]
val IV = new RomanNumeral[NumeralI[Numeral]](4) with NumeralI[Numeral]
val V = new RomanNumeral[NumeralI[Numeral]](5) with NumeralV[NumeralV[Numeral]]
val IX = new RomanNumeral[NumeralI[Numeral]](9) with NumeralI[Numeral]
val X = new RomanNumeral[NumeralV[Numeral]](10) with NumeralX[NumeralX[Numeral]]
val XX = new RomanNumeral[NumeralV[Numeral]](20) with NumeralX[NumeralX[Numeral]]
val XXX = new RomanNumeral[NumeralV[Numeral]](30) with NumeralX[NumeralX[Numeral]]
val XL = new RomanNumeral[NumeralV[Numeral]](40) with NumeralX[NumeralX[Numeral]]
val L = new RomanNumeral[NumeralX[Numeral]](50) with NumeralL[NumeralL[Numeral]]
}
EDIT:
Further question based of Victors answere. Ok, but what if I add upper and lower bounds to the type parameter so that B is a trait? E.g.
trait Bar
class Foo[T<:Bar](n:Int) {
def apply[B >: T <: Bar](f:Foo[B]) = {
new Foo[B](n + f.n) with B
}
}
Or maybe B can still be a class in this case? What if I know that the f argument too apply is of type Foo[B] with B? Is there a way to use that to mixin B with the return type?
ORIGINAL QUESTION BELOW
I want to mix in a trait I get as a type parameter when I create an object in Scala:
class Foo(val num:Int) {
def withM[B](foo:Foo) = new Foo(foo.num) with B
}
This results in a compile error:
error: class type required but B found
def withM[B](foo:Foo) = new Foo(foo.num) with B
^
I have also tried:
class Foo(val num:Int) {
def withM[B](foo:Foo) = new Foo(foo.num) with classOf[B]
}
But that does not work:
error: not found: type classOf
def withM[B](foo:Foo) = new Foo(foo.num) with classOf[B]
^
Is there someway to get around this? So that the return type of withM becomes Foo with B where B is the type parameter passed to withM
That kind of functionality simply isn't possible (or available)
Also, it's only possible to mixin traits (and interfaces), and your type parameter could be Int as far as the compiler knows.
You need to specify a concrete trait or interface type as so:
trait T
class Foo
object Test {
def apply = new Foo with T
}