scala3 extension method type parameter - scala

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

Related

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]

Scala method to convert generic Collection

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)

Generically adding implicits to both TreeSet and TreeMaps

I want to add some helpful implicits to both mutable and immutable TreeMaps and TreeSets in Scala.
Here is my attempt:
First try to define the least upper bound of TreeMap and TreeSet that has headOption/lastOption (from GenTraversableLike) and from/to/until (from Sorted):
type SortedCollection[A, Repr <: SortedCollection[A, Repr]] = collection.generic.Sorted[A, Repr] with collection.GenTraversableLike[A, Repr]
Write my util:
implicit class RichSortedCollection[A, Repr <: SortedCollection[A, Repr]](s: SortedCollection[A, Repr]) {
def greaterThanOrEqualTo(a: A): Option[A] = s.from(a).headOption
def lessThan(a: A): Option[A] = s.until(a).lastOption
def lessThanOrEqualTo(a: A): Option[A] = s.to(a).lastOption
}
This only works partially: SortedSet#greaterThan compiles but TreeMap#greaterThan does not. How do I fix it?
TreeMap[A, B] (transitively) extends GenTraversableLike[(A, B), TreeMap[A, B]] and Sorted[A, TreeMap[A, B]], so you could say it's a:
Sorted[A, TreeMap[A, B]] with GenTraversableLike[(A, B), TreeMap[A, B]]
This is close to your type alias, but the first type parameter of Sorted and GenTraverableLike in the type alias SortedCollection must be the same, which they are not above. They simply aren't compatible. That is, Repr = TreeMap[A, B] is fine, but A = (A, B) doesn't make sense.
You're going to have the same issue with all map types, and your only real choice is to re-implement RichSortedCollection for maps as well.

Parameterized type bounds

I have the following trait (simplified example)
trait F[A, M[_] <: Option[A]] {
def v: A
def f: A => M[A]
}
I want to be able to create the following trait
trait G[A] extends F[A, Some]
But this gives the following error
Error:(18, 20) type arguments [A,Some] do not conform to trait F's type parameter bounds [A,M[_] <: Option[A]]
How can I bound the M[_] parameterized type ?
Edit :
A type F[A, M[_] <: Option[_]] will work. But I actually have another function in my trait
trait F[A, M[_] <: Option[_]] {
def v: A
def f: A => M[A]
def f2: A => A = {
(a: A) => f(a).get
}
}
And in that case in f2, get doesn't return a value of type A even if f returns a M[A]
Error:(17, 20) type mismatch;
found : _$1
required: A
(a: A) => f(a).get
You can fully constrain the type of M since it doesn't really need to take type parameters:
trait F[A, M <: Option[A]] { // M no longer takes a type parameter
def v: A
def f: A => M
}
trait G[A] extends F[A, Some[A]] // Must specify M[A] since M doesn't take a type parameter
However you may want to provide some sort of mapping mechanism or something and go to some other type M[B] in which case a less constrained version would work better:
trait F[A, M[_] <: Option[_]] { // M is no longer constrained to A
def v: A
def f: A => M[A] // M is manually constrained to A here
}
trait G[A] extends F[A, Some]
UPDATE
Unfortunately your f2 function won't work. Even though we know that the types are correct, the compiler can't properly infer the type (limitations of Scala). You could add this function to G[A] and it will work as expected because M[_] has a concrete type with it now.
trait G[A] extends F[A, Some]{
def f3: A => A = a => f(a).get
}
And as a side note, you should never use Option.get, it defeats the purpose of using an Option! flatMap it or something :)

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[_].