Reputation: 651
I'm trying to define a method ConcatToList would be able to convert some objects of type T~T
or T~T~T
or T~T~T~T
or ...
to a List[T]
.
My problem comes with how I should go about defining the type of ConcatToList
def ConcatToList[T](concat: ~[T, ???]) = ...
what should I replace ??? by ?
Upvotes: 2
Views: 179
Reputation: 8462
The next code works for lists of mixed types:
case class ~~[T, G](l: T, r: G)
def concat[T, G](v: ~~[T, G]): List[T] = (v.l, v.r) match {
case (left, right : ~~[T, _]) => left :: concat(right)
case (left, right: T) => List(left, right)
}
println(concat(~~(2, 3))) // List(2, 3)
println(concat(~~(2, ~~(4, 5)))) // List(2, 4, 5)
println(concat(~~(2, ~~(4, ~~(5, 6))))) // List(2, 4, 5, 6)
println(concat(~~(2, ~~("s", 3.3)))) // List(2, s, 3.3)
// but
println(concat(~~(~~("s", 3.3), 2))) // List(~~(s, 3.3), 2)
P.S. I will leave this as an example of simple approach, but see Travis response for a better type-safer way
Upvotes: 0
Reputation: 139058
I'll assume a representation like this:
class ~[I, L](val init: I, val last: L)
That you instantiate like this:
scala> val x: Int ~ Int ~ Int ~ Int = new ~(new ~(new ~(1, 2), 3), 4)
x: ~[~[~[Int,Int],Int],Int] = $tilde@3e6fa38a
You're not going to be able to write your ConcatToList
method with a signature of the form you suggest, since there's no way to write a type constraint that describes exactly the types for which this operation is valid. Instead you can use a type class:
trait TildeToList[T] {
type Elem
def apply(t: T): List[Elem]
}
object TildeToList {
type Aux[T, E] = TildeToList[T] { type Elem = E }
implicit def baseTildeToList[E]: Aux[E ~ E, E] = new TildeToList[E ~ E] {
type Elem = E
def apply(t: E ~ E): List[E] = List(t.init, t.last)
}
implicit def recTildeToList[I, L](implicit ttl: Aux[I, L]): Aux[I ~ L, L] =
new TildeToList[I ~ L] {
type Elem = L
def apply(t: I ~ L): List[L] = ttl(t.init) :+ t.last
}
}
And then:
def tildeToList[T](t: T)(implicit ttl: TildeToList[T]): List[ttl.Elem] = ttl(t)
Which works like this:
scala> tildeToList(x)
res0: List[Int] = List(1, 2, 3, 4)
Note that what we get back is appropriately statically typed.
If you try to use it on a value that doesn't have an appropriate shape, you'll get a compile-time error:
scala> tildeToList(new ~('a, "a"))
<console>:16: error: could not find implicit value for parameter ttl: TildeToList[~[Symbol,String]]
tildeToList(new ~('a, "a"))
^
Which is presumably what you want.
Upvotes: 4