Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Factory method with dependent type

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.

like image 976
paradigmatic Avatar asked Aug 01 '26 03:08

paradigmatic


1 Answers

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.

like image 150
dk14 Avatar answered Aug 06 '26 10:08

dk14