Nathan Kronenfeld
Nathan Kronenfeld

Reputation: 513

Scala higher-kinded type issue with new collection class

I'm trying to write a homogenous tuple type, somewhat paralleling the built-in tuple type in scala.

I've got the following:

trait HomogeneousTupleFactory [H <: HomogeneousTuple[_, H]] {
  def makeHTuple[T] (values: Iterator[T]): HomogeneousTuple[T, H]
}
trait HomogeneousTuple [+T, H <: HomogeneousTuple[_, H]] extends Product {
  def getFactory: HomogeneousTupleFactory[H]
  def map [U] (fcn: T => U): HomogeneousTuple[U, H] = {
    getFactory.makeHTuple(
      this.productIterator.map(t => fcn(t.asInstanceOf[T]))
    )
  }
}

object HTuple2Factory extends HomogeneousTupleFactory[HTuple2[_]] {
  def makeHTuple[T] (values: Iterator[T]): HTuple2[T] = {
    new HTuple2(values.next, values.next)
  }
}
class HTuple2[+T] (t1: T, t2: T) extends Tuple2(t1, t2) 
    with HomogeneousTuple[T, HTuple2[_]] {
  def getFactory = HTuple2Factory
}

I'm trying to get it so that HTuple2.map[U] returns an HTuple2[U] instead of a HomogeneousTuple[U, HTuple2] (which is legitimate and correct, but less convenient), but I can't get it to work.

Anyone have any clues how to do this? Is there a better way than what I'm doing?

Upvotes: 1

Views: 54

Answers (2)

Christophe Calv&#232;s
Christophe Calv&#232;s

Reputation: 692

I don't have time to develop much but it may seem that what you're looking for is

sealed abstract class Nat
final abstract class Z           extends Nat
final abstract class S[n <: Nat] extends Nat

trait Vect[n <: Nat, +A] {
  def map[B](f : A => B) : Vect[n , B]
}

final case object VNil extends Vect[Z, Nothing] {
  def map[B](f : Nothing => B) : Vect[Z, B] = this
}

final case class VCons[n <: Nat, A](head : A, tl : Vect[n, A]) extends Vect[S[n], A] {
  def map[B](f : A => B) : Vect[S[n], B] = VCons[n, B](f(head), tl.map(f))
}

implicit final class ConsOps[A](val self : A) extends AnyVal {
  def +++[n <: Nat](l : Vect[n, A]) : Vect[S[n], A] = VCons(self, l)
}

type _0 = Z
type _1 = S[_0]
type _2 = S[_1]

type Tuple0[A] = Vect[_0, A]
type Tuple1[A] = Vect[_1, A]
type Tuple2[A] = Vect[_2, A]

def inc[n <: Nat](v : Vect[n , Int]) : Vect[n , Int] = v.map((i : Int) => i + 1)

inc(2 +++ (3 +++ VNil))

Upvotes: 0

Angelo Genovese
Angelo Genovese

Reputation: 3398

I had to move a few things around, but this seems to work:

trait HomogeneousTupleFactory [H[_] <: HomogeneousTuple[_, H]] {
  def makeHTuple[T] (values: Iterator[T]): H[T]
}

trait HomogeneousTuple [+T, H[_] <: HomogeneousTuple[_, H]] extends Product {
  def getFactory: HomogeneousTupleFactory[H]
  def map [U] (fcn: T => U): H[U] = {
    getFactory.makeHTuple(
      this.productIterator.map(t => fcn(t.asInstanceOf[T]))
    )
  }
}

object HTuple2Factory extends HomogeneousTupleFactory[HTuple2] {
  def makeHTuple[T] (values: Iterator[T]): HTuple2[T] = {
    new HTuple2(values.next, values.next)
  }
}
class HTuple2[+T] (t1: T, t2: T) extends Tuple2(t1, t2)
  with HomogeneousTuple[T, HTuple2] {
  def getFactory = HTuple2Factory
}

Basically you needed the H type param in HomogeneousTuple to be a higher-kinded type, the rest of the changes flowed out of that.

Upvotes: 2

Related Questions