Scala method to convert generic Collection - scala

How can I create a method that takes a generic Collection M[A] and a function from A to B and returns a Collection M[B], using the map method?
Something like:
def convert[A, M[X] <: Traversable[X], B](in: M[A], f: A => B): M[B] =
in.map(f)
The method above fails to compile with: type mismatch; found : Traversable[B] required: M[B]. Since the static type of Traversable[A].map(f: A => B) is Traversable[B] as pointed by Oleg Pyzhcov
Note: The purpose of this method is not just mapping the collection, this is just a simplification.

The full signature of map is
def map[B, That](f: (A) ⇒ B)(implicit bf: CanBuildFrom[Repr, B, That]): That
So you need to provide that CanBuildFrom from the call site and make sure that Repr is inferred to your concrete type M[A] by using collection traits that end on Like and have two type parameters
import scala.collection.generic.CanBuildFrom
import scala.collection.TraversableLike
import scala.language.higherKinds
def convert[M[x] <: TraversableLike[x, M[x]], A, B](
in: M[A],
f: A => B
)(implicit
cbf: CanBuildFrom[M[A], B, M[B]]
): M[B] = in.map(f)(cbf)

Related

scala3 extension method type parameter

this is a direct translation of my scala2 code to scala3
trait Narrow[F[_], A, B <: A: ClassTag]:
def apply(fa: F[A]): F[B]
extension [F[_], A] (fa: F[A]):
def narrow[B: ClassTag] (using op: Narrow[F, A, B]): F[B] = op(fa)
I need to specify the type of the narrow operation at the call site, however extension methods do not allow that syntax. what are there the best workarounds for this limitation?
the purpose of this is to be able to narrow the type in a collection / try / whatever. the narrow type class will flatmap whatever is inside, compare the runtime type, if it matches wrap that B in an F, or otherwise return an empty F
trait A
trait B extends A
object A extends A
object B extends B
val bb: List[B] = List(A, A, B, B, A)
.narrow[B]
assert(bb == List(B, B))
You could use a polymorphic function, if you can handle the ugliness:
extension [F[_], A] (fa: F[A]):
def narrow() = [B <: A] => (using op: Narrow[F, A, B]) => op(fa)
You can then call it with foo.narrow()[String]. Here it is in Scastie.
The narrow() is necessary, because without it, the type argument would go to the extension and not the polymorphic function.
In the future, Scala 3 may allow type arguments directly to methods inside extensions, but right now, you can keep using your Scala 2 implicit class and change it after the next release:
implicit class NarrowOps[F[_], A](fa: F[A]):
def narrow[B <: A](using op: Narrow[F, A, B]) = op(fa)
Scastie
Side note: You don't need B: ClassTag again in your extension, although I believe you do need to use the bound B <: A.
I wasnt able to live with the () on the call site. I decided trying a implicit conversion to a type with an apply method with just a type parameter.
trait NarrowTypeClass[F[_], A, B <: A: ClassTag]:
def apply(fa: F[A]): F[B]
given [F[_], A] as Conversion[F[A], Narrowable[F, A]] = Narrowable(_)
sealed class Narrowable [F[_], A] (fa: F[A]):
def narrow[B <: A: ClassTag] (using op: NarrowTypeClass[F, A, B]): F[B] = op(fa)
this seems to do the trick

Numeric Map Over With Functor

I want to map case class Bonus[A: Numeric](amt: A) over a Functor and it fails. The compilation error is
Error:(157, 69) could not find implicit value for evidence parameter of type Numeric[B] (No implicit Ordering defined for B.)
override def fmap[A, B](fa: Bonus[A])(f: A => B): Bonus[B] = Bonus(f(fa.amt))
In general, I want to fix the parameter type in Bonus to numbers. How do I do fix that? Thanks
The code snippet,
trait Functor[F[_]] {
def fmap[A, B](fa: F[A])(f: A => B): F[B]
}
def fmap[A, B, F[_]](fa: F[A])(f: A => B)(implicit ev: Functor[F]): F[B] = ev.fmap(fa)(f)
case class Bonus[A: Numeric](amt: A)
implicit val bonusFunctor = new Functor[Bonus] {
override def fmap[A, B](fa: Bonus[A])(f: A => B): Bonus[B] = Bonus(f(fa.amt)) // error
}
fmap(Bonus(123))(_ * 2)
Update 1
Thank you Mario & Dmytro for your answers.
Dmytro, your answer is precisely as what I found at https://users.scala-lang.org/t/how-to-add-type-constraint-to-functors-map-function/2055. It makes sense that either I drop the constraint or I use a constraint Functor. I accepted Mario's answer because it shows me an alternative solution because it is not possible with Functor.
Instance of Functor type class can be defined not for any type constructor.
The fact that type constructor F[_] has instance of type class Functor means that for any A, B, having a function A => B, you know how to transform F[A] to F[B].
But how to transform Bonus[A] to Bonus[B] for any A, B?
Types Bonus[A], Bonus[B] make sense for any A, B, even if they are not Numeric, but creating new instance of Bonus[B] via constructor makes sense only for B: Numeric.
Either remove context bound Numeric in case class Bonus[A: Numeric](amt: A) or Bonus is not a Functor.
If you define your own type class
trait NumericFunctor[F[_]] {
def fmap[A: Numeric, B: Numeric](fa: F[A])(f: A => B): F[B]
}
it will be not the standard functor over category of types but a custom functor over category of Numeric types.
Try
trait GFunctor[F[_], G[_]] {
def fmap[A, B](fa: F[A])(f: A => B)(implicit ga: G[A], gb: G[B]) : F[B]
}
def fmap[A, B, F[_], G[_]](fa: F[A])(f: A => B)(implicit ev: GFunctor[F, G], ga: G[A], gb: G[B]): F[B] = ev.fmap(fa)(f)
case class Bonus[A: Numeric](amt: A)
implicit val bonusFunctor = new GFunctor[Bonus, Numeric] {
override def fmap[A, B](fa: Bonus[A])(f: A => B)(implicit numA: Numeric[A], numbB: Numeric[B]): Bonus[B] = Bonus(f(fa.amt))
}
fmap(Bonus(123))(_ * 2)
which outputs
res0: Bonus[Int] = Bonus(246)
Note how we have made our typeclass solution aware of further bounds on A and B via
(implicit ga: G[A], gb: G[B])

generic to specific collection after partial function application

I am trying to apply a partial function to a generic collection while returning a specific collection instead of Traversable of the original type.
Here's what I've tried (among other things):
def filter[A, B, C <: Traversable[A],Y<:B, D <: Traversable[Y]](xs: Option[D] with TraversableLike[A,Option[D]])(pf:PartialFunction[A,B]): D = {
xs.collect(pf)
}
def filter[A, B, C <: Traversable[A], D <: Traversable[B]](xs: D with TraversableLike[A,D])(pf:PartialFunction[A,B])(implicit ev: B =:= Option[Double]): D = {
xs.collect(pf)
}
Any ideas why implicit ev: B =:= Option[Double] doesn't find the correct types?
I got inspiration from these posts:
Returning original collection type in generic method
How do I apply the enrich-my-library pattern to Scala collections?
Writing my own generic map functioni
You can do this:
import scala.collection.Iterable
import scala.collection.generic.CanBuildFrom
def collect[C[_], A, B](col: C[A])
(pf: PartialFunction[A, B])
(implicit ev: C[A] <:< Iterable[A],
cbf: CanBuildFrom[Nothing, B, C[B]]): C[B] =
col.iterator.collect(pf).to[C]

Composing F-Algebra[F, A] to F-Algebra[F, Seq[A]]

I'm trying to compose F-Algebras like in this page. The difference is that, instead of composing with a tuple, like this:
type FAlgebra[F[_], A] = F[A] => A
def algebraZip[F[_], A, B](fa: FAlgebra[F, A], fb: FAlgebra[F, B])
(implicit F: Functor[F]): FAlgebra[F, (A, B)] =
fab => {
val a = fa(fab.map(_._1))
val b = fb(fab.map(_._2))
(a, b)
}
I would like to use a Seq, like this:
def algebraSeq[F[_], A](fa: FAlgebra[F, A])
(implicit F: Functor[F]): FAlgebra[F, Seq[A]] = ???
Is it possible? What would I need? Or would using shapeless HList help?
If I can make some slight alterations to your constraints, I can find an implementation:
def algebraSeq[F[_]: Traverse, A](fa: FAlgebra[F, A]): FAlgebra[F, Seq[A]] =
fseq => fseq.sequence.map(f => fa(f))
I needed the Traverse instance to be able to sequence a F[Seq[A]] to a Seq[F[A]].
In the past you had to write this function for List instead of Seq because there was no Applicative[Seq] instance. But since cats 2.3.0 instances for immutable.Seq (which is the default scala.Seq in Scala 2.13) were added.
Completely agree with Jasper's answer. But I like to generalise ;)
The shape of your algebra is like CoKleisli:
import cats._
import cats.implicits._
import cats.data.Cokleisli
type FAlgebra[F[_], A] = Cokleisli[F, A, A]
def nestEffect[F[_], A, B, G[_]](coKleisli: Cokleisli[F, A, B])
(implicit F: Traverse[F], G: Applicative[G]): Cokleisli[F, G[A], G[B]] =
Cokleisli((fga: F[G[A]]) => F.sequence[G, A](fga).map(coKleisli.run))
def algebraSeq[F[_], A](fa: FAlgebra[F, A])
(implicit F: Traverse[F]): FAlgebra[F, List[A]] =
nestEffect[F, A, A, List](fa)

How does Scalaz `F[_] : Applicative` type constraint imply use of implicit parameters?

I am struggling to understand the following function definition in Traverse trait in Scalaz:
def traverse[F[_] : Applicative, A, B](f: A => F[B], t: T[A]): F[T[B]]
The part I don't understand is F[_] : Applicative.
Now, let's see what Applicative is:
trait Applicative[Z[_]] extends Pointed[Z] with Apply[Z] {
override def fmap[A, B](fa: Z[A], f: A => B): Z[B] = this(pure(f), fa)
override def apply[A, B](f: Z[A => B], a: Z[A]): Z[B] = liftA2(f, a, (_:A => B)(_: A))
def liftA2[A, B, C](a: Z[A], b: Z[B], f: (A, B) => C): Z[C] = apply(fmap(a, f.curried), b)
}
Here, for traverse to work for some type F, one needs to bring an implicit object of type Applicative[F] in scope.
I'd like to understand several things:
Wat exactly does F[_] : Applicative mean?
Why does F[_] has something to do with Applicative? We need Applicative[F], not F[something] extends Applicative right?
Why does this method use implicit values of type Applicative[F] without declaring implicit parameters?
I think all three questions can be answered with the fact that this notation:
def traverse[F[_] : Applicative, A, B](f: A => F[B], t: T[A]): F[T[B]]
is equivalent to this:
def traverse[F[_], A, B](f: A => F[B], t: T[A])(implicit $ev: Applicative[F]): F[T[B]]
The first notation is known as a context bound for F[_].