Reputation: 5681
I'd like to make a method that take a vector or a seq of of some type, let's say Int, and calls filter on it.
For example:
implicit class SharedOps[F](xs: F)(implicit ev: OneOf[F, Seq[Int] ::: Vector[Int] ::: HSNil]) {
def filter(x: Int):F = xs.filter({a:Int => a == x})
}
OneOf basically checks that F is either a Seq[Int] or a Vector[Int]
The tricky part is that I want the filter to return the same type as the input (e.g. Seq[Int] or Vector[Int]) but compiler is complaining
error: type mismatch;
found : scala.this.Function1[scala.this.Int,scala.this.Boolean]
required: scala.this.Int
def filter(x: Int):F = xs.filter({a:Int => a == x})
Somehow compiler forgot I started with a collection of sorts and thinks xs is a single thing.
So I changed the design:
implicit class SharedOps2[A,F[A]<:TraversableLike[A,A]](xs: F[A])(implicit ev: OneOf[F[A], Seq[Int] ::: Vector[Int] ::: HSNil]) {
def filter(x: A): F[A] = xs.filter({ a: Int => a == x })
}
Now compiler is complaining that: Expression of type A doesn't conform to expected type F[A]
Not sure how to take it from here. I'd like to avoid shapeless coproducts at this point.
For completeness here's the OneOf code:
sealed trait HSubset // HList
@implicitNotFound("No member of type class HSubset in scope for ${H}")
trait :::[+H, +T <: HSubset] extends HSubset // HCons ::
sealed trait HSNil extends HSubset // HNil
@implicitNotFound("No member of type class BelongsTo in scope for ${T}")
trait BelongsTo[T, U <: HSubset]
object BelongsTo {
implicit def baseCase[H, U <: HSubset]: BelongsTo[H, H ::: U] = null
implicit def recursiveCase[H, U <: HSubset, T](implicit ev: T BelongsTo U): T BelongsTo (H ::: U) = null
}
@implicitNotFound("No member of type class SubsetOf in scope for ${U} ${T}")
trait SubsetOf[U <: HSubset, T <: HSubset]
object SubsetOf {
implicit def baseCase[U1, U <: HSubset](implicit s: U1 BelongsTo U): SubsetOf[U1 ::: HSNil, U] = null
implicit def recursiveCase[U <: HSubset, T1, T <: HSubset](implicit ev1: T1 BelongsTo U, ev2: T SubsetOf U): (T1 ::: T) SubsetOf U = null
}
trait OneOf[T, U <: HSubset]
object OneOf {
implicit def baseCase[U <: HSubset, T](implicit s: T BelongsTo U): T OneOf U = null
implicit def recursiveCase[T, Ev <: HSubset, Target <: HSubset](implicit ev1: T OneOf Ev, ev2: Ev SubsetOf Target): T OneOf Target = null
}
Upvotes: 0
Views: 98
Reputation: 15770
TraversableLike.filter
returns Repr
, which is type passed as second type parameter to TraversableLike
. You need F[A]
there instead of A
. This compiles for me:
implicit class SharedOps2[A,F[A]<:TraversableLike[A,F[A]]](xs: F[A])(implicit ev: OneOf[F[A], Seq[A] ::: Vector[A] ::: HSNil]) {
def filter(x: A): F[A] = xs.filter({ a: A => a == x })
}
Note also type of a
changed to A
, because this is the type inside F[A]
collection.
Upvotes: 1
Reputation: 22840
This is the proposed typeclas.
My advice would be to use specific types like List
& Vector
instead of Seq
.
trait Filter[F[_]] {
def filter[A](fa: F[A])(p: A => Boolean): F[A]
}
object Filter {
implicit final val VectorFilter: Filter[Vector] =
new Filter[Vector] {
override final def filter[A](vector: Vector[A])(p: A => Boolean): Vector[A] =
vector.filter(p)
}
implicit final val SeqFilter: Filter[Seq] =
new Filter[Seq] {
override final def filter[A](seq: Seq[A])(p: A => Boolean): Seq[A] =
seq.filter(p)
}
}
object syntax {
object filter {
implicit class FilterOps[F[_], A](private val fa: F[A]) extends AnyVal {
@inline
final def filter(p: A => Boolean)(implicit ev: Filter[F]): F[A] =
ev.filter(fa)(p)
}
}
}
import syntax.filter._
def foo[F[_] : Filter](xs: F[Int]): F[Int] =
xs.filter(i => (i % 2) == 0)
Which you can use like:
foo(Vector(1, 2, 3))
// res: Vector[Int] = Vector(2)
foo(List(1, 2, 3))
// could not find implicit value for evidence parameter of type ammonite.$sess.cmd0.Filter[List]
foo(Seq(1, 2, 3))
// res: Seq[Int] = List(2)
BTW, it is worth mentioning that such typeclass already exists in cats
Upvotes: 2