2

I currently have:

class X[+T: Numeric](val x: T)
abstract class M[N: Numeric, T <: X[N]] { // <- I'd like to remove N.
  def apply(x: Int): T
  final def row = (1 to 10).map(this(_))
}

I use it like this:

class Y(x: Double, val y: Double) extends X[Double](x)
class Z extends M[Double, Y] {           // <- So that this is simpler.
  def apply(x: Int) = new Y(0.0, 0.0)
}

It works like this:

object testapp {
  // row is properly polymorphic, allowing access to Y.y
  def main(args: Array[String]): Unit = (new Z).row.map(r => println(r.y))
}

I want Z to be simpler so that I can use M like:

class Z extends M[Y] {
  def apply(x: Int) = new Y(0.0, 0.0)
}

or, even better:

class Z extends M[Double] {           // i.e. Meaning apply can return
  def apply(x: Int) = new Y(0.0, 0.0) // any subclass of X[Double]
}

Here are my Gist iterations to reach this point.

nix
  • 378
  • 1
  • 8

2 Answers2

2

A third way in type params vs type members is to use both.

An advantage of a type member is that it doesn't pollute the signature of child classes. The type member can remain abstract if it is superfluous (even in a concrete class); and only the bottom class must define it if necessary.

  import scala.collection.immutable.IndexedSeq
  class X[+T: Numeric](val x: T)
  abstract class M[+A: Numeric] {
    type W <: X[A]
    def apply(x: Int): W
    final def row: IndexedSeq[W] = (1 to 10) map apply
    def sumx: A = {  // in terms of the underlying Numeric
      val n = implicitly[Numeric[A]]
      n fromInt (0 /: row)((s,w) => s + (n toInt w.x))
    }
  }

  class Y(x: Double, val y: Double) extends X[Double](x)
  class Z extends M[Double] {
    type W = Y
    def apply(x: Int) = new Y(0.0, 0.0)
  }

  def main(args: Array[String]): Unit = (new Z).row foreach (Console println _.y)
som-snytt
  • 39,429
  • 2
  • 47
  • 129
  • Chosen since answered the question most directly. Thanks. Can you clarify for me? I am reading it as : `type W <: X[A]` defines a type `W` within `M` such that `W` is a subtype of `X[A]`. Then in subtypes of `M`, `W` is "overriden" to be the type needed by the subtype of `M`? – nix Dec 16 '12 at 18:04
  • 1
    With the bound, type W is still abstract, so no override is required when you define it. (Like any other member.) (Spec 4.3. Also, beware http://stackoverflow.com/a/10223364/1296806.) – som-snytt Dec 17 '12 at 00:26
1

You didn't really need class M here:

class X[+T: Numeric](val x: T)
def row[W <: X[_]](c: => Int => W) = (1 to 10).map(c)

class Y(x: Double, val y: Double) extends X[Double](x)
def z = row(_ => new Y(0.0, 0.0))

def main(args: Array[String]): Unit = z.map(r => println(r.y))

If you want to keep M, you use same idea:

class X[+T: Numeric](val x: T)
abstract class M[W <: X[_]] {
    def apply(x: Int): W
    final def row = (1 to 10).map(this(_))
}

class Y(x: Double, val y: Double) extends X[Double](x)
class Z extends M[Y] {
  def apply(x: Int) = new Y(0.0, 0.0)
}

def main(args: Array[String]): Unit = (new Z).row.map(r => println(r.y))
Sergey Passichenko
  • 6,920
  • 1
  • 28
  • 29
  • Thanks for the answer but the goal isn't to eliminate `M`. I think I need it (but i'm considering your alternative). It's part of a larger solution. The idea is that `M` is a collection that can only contain types bounded by `X[T]` where `T` is `Numeric`. I create several subclasses of `M` to encapsulate different functionality against the elements. – nix Dec 16 '12 at 03:08
  • Instead of several subclasses you can create several functions like z. I can miss something without knowledge of larger solution, but I think OO encapsulation is overhead here. – Sergey Passichenko Dec 16 '12 at 03:14
  • The problem I have with the function-approach is that `M` is actually a type, specifically, a collection. But i think you might be right that i should provide functions that operate on the single parameterized type `M` rather than subclassing `M`. One problem with your answer is `X[_]`. `_` is `Any` but i need `Numeric`. – nix Dec 16 '12 at 04:37
  • 1
    It will not compile with X[T] that have not implementation for Numeric typeclass in scope. Check it with 'class StringX(x: String) extends X[String](x)' – Sergey Passichenko Dec 16 '12 at 05:02