Parameterized type bounds - scala

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 :)

Related

Why does the implicit derivation of this poly function case fails without subtype evidence?

There are two similar shapeless polymorphic functions in the following example. The only difference between them is that deserSucceeding's implicit case definition has an additional subtyping evidence (implicit e: FS <: FromStr[T]). Scala fails to derive an implicit Aux.Case for deserFailing, but succeeds for deserSucceeding.
Why? Is this a limitation of the scala compiler or can deserSucceeding lead to an ambiguity in the implicit derivation / type inference?
import shapeless._
type FromStr[T] = String => T
object FromDouble extends FromStr[Double] {
override def apply(v1: String): Double = v1.toDouble
}
object deserFailing extends Poly2 {
implicit def kase[T, FS <: FromStr[T]]: Case.Aux[String, FS, T] = at((s, fs) => fs(s))
}
// fails to derive a `Case.Aux`
deserFailing("1.0", FromDouble)
object deserSucceeding extends Poly2 {
implicit def kase[T, FS](implicit e: FS <:< FromStr[T]): Case.Aux[String, FS, T] = at((s, fs) => fs(s))
}
deserSucceeding("1.0", FromDouble)
The TL;DR; is how type inference works.
When you do [T, FS <: FromStr[T]] the compiler tries to infer both at the same type and as such it probably end up inferring Any or Nothing from one of those to make the subtype constraint to type-check.
Whereas the second option doesn't force any subtype restriction during inference which makes the compiler to infer better types and then after that it will check the subtyping constraint.
It is similar to this example:
def tupleIfSubtype1[T <: U, U](t: T, u: U) = (t, u)
def tupleIfSubtype2[T, U](t: T, u: U)(implicit ev: T <:< U) = (t, u)
tupleIfSubtype1("Foo", 3) // compiles, it infers Any for the second element.
tupleIfSubtype2("Foo", 3) // doesn't compile.
More in-depth explanation, here.

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

diverging implicit expansion for type Ordering[Any] with recursion

I am trying to implement BST in Scala. I want to add a node to binary tree by '+' operator. However I get two errors during compilation time
Error:(39, 46) diverging implicit expansion for type Ordering[Any]
starting with method orderingToOrdered in object Ordered
def +[A: Ordering](v: A) : Tree[Any] = add(this, v)
Error:(39, 46) not enough arguments for method add: (implicit evidence$1: Ordering[Any])A$A47.this.Tree[Any].
Unspecified value parameter evidence$1.
def +[A: Ordering](v: A) : Tree[Any] = add(this, v)
Here is the code.
sealed trait Tree[+A]{
def add[A: Ordering](tree: Tree[A], v: A) : Tree[A] = tree match {
case EmptyTree => Node(EmptyTree, v, EmptyTree)
case Node(l, x, r) => {
if(x.compare(v) == 1)
Node(add(l, v), x, r)
else
Node(l, x, add (r, v))
}
}
def +[A: Ordering](v: A) : Tree[Any] = add(this, v)
}
Can someone help me and tell what is wrong?
I think the problem is the interface of the operator +.
def add[A: Ordering](tree: Tree[A], v: A) : Tree[A]
The add function accepts a Tree[A] and returns a Tree[A] where A is constrained by the typeclass Ordering. Nothing wrong here (but it would better to define it in the companion object of Tree).
def +[A: Ordering](v: A) : Tree[Any] = add(this, v)
The + function accepts a value (you're assuming that the tree parameter of add is this here) of type A which is also constrained by the typeclass Ordering.
The first issue here is, that you return a Tree[Any]. I'm assuming you meant Tree[A].
The second issue is, that the A in the function + is not the type parameter A which this is referring to. So you probably meant:
sealed abstract class Tree[+A: Ordering] {
...
def +(v: A) : Tree[A] = add(this, v)
}
If this is not what you want, you have to constrain A in the function + by the typeclass Ordering:
sealed trait Tree[A] {
...
def +(v: A)(implicit ev: Ordering[A]): Tree[A] = add(this, v)
}
In this case A may not be covariant anymore, because it occurs in an invariant position.
Edit: As mentioned below, there is also the opportunity of introducing a new type B in the function +. The overall code could look like this:
object Tree {
def add[A](tree: Tree[A], v: A)(implicit ev: Ordering[A]): Tree[A] = ...
}
sealed trait Tree[+A] {
def +[B >: A](v: B)(implicit ev: Ordering[B]): Tree[B] = Tree.add(this, v)
}
... implementation of trait Tree
2nd Edit: In the example above it is necessary to introduce a super type and not a sub type B, because the function Tree.add would need the typeclass Ordering[A] - which we can not provide.
The following makes no sense, because it only proves that values of type B can be ordered.
def +[B <: A](v: B)(implicit ev: Ordering[B]): Tree[B] = ???
And this does not even compile, because A occurs in an invariant position.
def +[B <: A](v: B)(implicit ev: Ordering[A]): Tree[B] = ???
So to achieve that A does not occur in an invariant position and to prove that values of type B as well as values of type A can be ordered, we have to introduce B as a super type.

Can an existentially quantified type variable be forced to have only a single type?

Consider the following code
trait Foo[T] {
def one: Foo[_ >: T]
def two: T
def three(x: T)
}
def test[T](f: Foo[T]) = {
val b = f.one
b.three(b.two)
}
The method test fails to type check. It says:
found : (some other)_$1(in value b)
required: _$1(in value b)
val x = b.three(b.two)
If I am interpreting this correctly, the compiler thinks that b in method test has a type that looks like this (not legal syntax, but hopefully clearer):
trait Foo {
def two: X where ∃ X >: T
def three(x: X where ∃ X >: T)
}
What I was hoping for was that it would have a type like this:
∃ X >: T such that trait Foo {
def two: X
def three(x: X)
}
The intent being that while the precise type X is not known, the compiler knows that its the same unknown type being returned by "two" and expected by "three". This seems different from what happens with normal univerally quantified generics. The following compiles, but exposes the type parameter X which I want to hide as it will vary between instances of Foo:
trait Foo[T] {
def one[X >: T]: Foo[X]
def two: T
def three(x: T)
}
def test[T, X >: T](f: Foo[T]) = {
val b = f.one[X]
b.three(b.two)
}
Is there a way to get the same behaviour for existentially quantified generics we get when they're univerally quanified?
def one: Foo[_ >: T] is equivalent to
def one: Foo[U >: T] forSome {type U >: T}
this one instead works
def one: Foo[U forSome {type U >: T}]
I do not however understand why this would make a difference. It seems like it should not to me. (shrug)
The problem is the compiler thinks that
b.two: _>:T
b.three(_>:T)
i.e. two is a supertype of T and three requires a supertype of T. But a supertype of T is not necessarily assignment compatible with another supertype of T, as in this example:
A >: B >: C
def get:A
def put(B)
put(get) // type mismatch
So if all the information we have is that they are supertypes of T then we cannot do this safely.
We have to explicitly tell the compiler that they are the same supertype of T.
trait Foo[T] {
type U <: T
def one: Foo[U]
def two: T
def three(x: T)
}
Then just set U when you implement the trait:
val x = new Foo[Dog]{
type U = Mammal
...
I would prefer this approach over the existential types due to the cleaner syntax and the fact that this is core Scala and does not need the feature to be imported.

Scala: F-Bounded Polymorphism Woes

Assume the existence of the following types and method:
trait X[A <: X[A]]
case class C extends X[C]
def m(x: PartialFunction[X[_], Boolean])
I want to be able to create a PartialFunction to be passed into m.
A first attempt would be to write
val f: PartialFunction[X[_], Boolean] = {
case c: C => true
}
m(f)
This fails with type arguments [_$1] do not conform to trait X's type parameter bounds [A <: X[A]]. So, it seems we have to constraint X's type parameters.
A second attempt:
val f: PartialFunction[{type A <: X[A]}, Boolean] = {
case c: C => true
}
m(f)
This fails on the application of m because PartialFunction[AnyRef{type A <: X[this.A]},Boolean] <: PartialFunction[X[_],Boolean] is false.
Is there any way not involving casting that actually satisfies the compiler both on the definition of the partial function and on the application of m?
I'm not sure what exactly you want, but since you are using an existential type (in disguise of the _ syntax), this is how you can make that work:
val f: PartialFunction[X[A] forSome {type A <: X[A]}, Boolean] = {
case c : C => true
}
The _ syntax isn't good enough here, since you need to give the existential type the right upper bound. That is only possible with the more explicit forSome syntax.
What I find surprising, though, is that Scala accepts the declaration
def m(x: PartialFunction[X[_], Boolean])
in the first place. It seems weird that it even considers X[_] a well-formed type. This is short for X[A] forSome {type A <: Any}, which should not be a valid application of X, because it does not conform to the parameter bounds.
Not sure if that's what you wanted to achieve, but this is the working sequence:
trait X[A <: X[A]]
case class C extends X[C]
def m[T<:X[T]](x: PartialFunction[X[T], Boolean]) = print("yahoo!")
scala> def f[T<:X[T]]:PartialFunction[X[T], Boolean] = {
| case c: C => true
| }
f: [T <: X[T]]=> PartialFunction[X[T],Boolean]
scala> m(f)
yahoo!