Adrian
Adrian

Reputation: 5681

how to call filter on both Seq an Vector

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

Answers (2)

amorfis
amorfis

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.

fiddle

Upvotes: 1

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

Related Questions