Reputation: 757
I have a trait like the following
private class SeqConverter[T](implicit val conv : Converter[T]) extends Converter[Seq[T]] {
def toJs(x: Seq[T]): JsAny = {
x.foldLeft(JsArray[JsAny]()) { (acc, next) =>
acc.+:(conv.toJs(next))
}
}
def toScala(x: JsAny): Seq[T] = {
val arr = x.asInstanceOf[JsArray[JsObject]]
var lst = List[T]()
arr foreach { x =>
lst = conv.toScala(x) :: lst
}
lst
}
}
I want to have something more like this
private class SeqConverter[T, F <: Seq[T]](implicit val conv : Converter[T]) extends Converter[F] {
def toJs(x: F): JsAny = {
x.foldLeft(JsArray[JsAny]()) { (acc, next) =>
acc.+:(conv.toJs(next))
}
}
def toScala(x: JsAny): Seq[T] = {
//need to construct empty F here to build it
}
}
But the problem is I have no way to get a member of F in order to start preforming construction of it. Is there any way I could get this to exist? It seems like there should be some kind of way to construct an empty member of F so so that I could use +: in order to convert from 1 kind of sequence to another. Does anything like that exist?
Upvotes: 2
Views: 205
Reputation: 38217
UPDATE: if you want to avoid depending on Scalaz, you can define your own type class and instances thereof:
import scala.language.higherKinds
trait Coll[TS[_], T] {
def zero: TS[T]
def append(a: TS[T], b: TS[T]): TS[T]
def point(x: T): TS[T]
}
object Coll {
implicit def listOfTIsColl[T] = new Coll[List, T] {
def zero = Nil
def append(a: List[T], b: List[T]) = a ++ b
def point(x: T) = List(x)
}
implicit def vectorOfTIsColl[T] = new Coll[Vector, T] {
def zero = Vector.empty
def append(a: Vector[T], b: Vector[T]) = a ++ b
def point(x: T) = Vector(x)
}
}
def foo[T, U, TS[_]](xs: TS[T], x: U)(implicit
coll: Coll[TS, T],
ev1: TS[T] <:< Seq[T],
ev2: U =:= T
) = {
(coll.zero, coll.append(coll.zero, coll.point(x)))
}
assert(foo(Vector(1, 2, 3), 4) == (Vector(), Vector(4)))
// foo(Vector(1, 2, 3), 4.4) -- error: Cannot prove that Double =:= Int
// foo(Vector(1, 2, 3), "hello") -- error: Cannot prove that String =:= Int
Note that it's necessary for T
and U
to be separate type parameters; with def foo[T, TS[_]](xs: TS[T], x: T) ...
, you'd be able to use foo
as expected, but things like foo(Vector(1, 2, 3), "hello")
would work and the type inferencer would infer a type like Vector[Any]
. However, with the above, stricter definition of foo
, this won't be allowed, which is, at least in idiomatic functional code, desirable.
Scalaz based solution: Scalaz Monoid and Applicative will help you:
import scalaz._
import Scalaz._
scala> Monoid[List[Int]].zero
res0: List[Int] = List()
scala> Monoid[Vector[Int]].zero
res1: Vector[Int] = Vector()
scala> Monoid[Vector[Int]].append(Vector(1, 2), Vector(3, 4))
res2: Vector[Int] = Vector(1, 2, 3, 4)
and
scala> Applicative[Vector].point(1)
res0: Vector[Int] = Vector(1)
Then, combining Monoid
and Applicative
will give you all of zero
, append
, and point
/pure
:
def foo[T, TS[_], U](xs: TS[T], x: U)(implicit
monoid: Monoid[TS[T]],
applic: Applicative[TS],
ev1: TS[T] <:< Seq[T],
ev2: U =:= T
) = {
(monoid.zero,
monoid.append(monoid.zero, applic.point(x)))
}
then:
> foo(Vector(1, 2, 3), 4)
res1 = (Vector(),Vector(4))
I'm not confident there aren't any conciser solutions, e.g. one that relies on just one type class, but this one seems to work correctly.
Upvotes: 1