I'm trying to achieve something with ScalaTest and based on C. Hosrtmann's "Scala for the impatient" I think this is related to "Family polymorphism". Apparently I don't understand something about Scala's type system, so I fail.
To cut the long story short, this is what I'm trying to do.
I have a base (production) trait:
trait MaxPQ[Key] {
implicit protected val cmp: Ordering[_ >: Key]
...
def insert(v: Key): Unit
def max(): Key
def delMax(): Key
...
}
then there is a number of implementations (that use either a backing tree or an array).
In tests I want to create an abstract structure which would allow to test any implementation for three Ordered Keys: Char, Int, Double.
First I've written two behaviors (for empty and non-empty priority queues). Here's a snippet:
trait MaxPQBehaviours {
// underlying tests spec
self: BaseSpec =>
def nonEmptyMaxPQ[T <: Ordered[T], ImplLà <: MaxPQ[T]](instanceSupplier: () => ImplLà, sortedInput: List[T]): Unit = {
...
behavior of "size"
it should s"be equal to ${sortedInput.size}" in {
val instance = instanceSupplier()
instance.size() shouldEqual sortedInput.size
}
behavior of "max"
it should s"return the expected $max" in {
val instance = instanceSupplier()
instance.max() shouldEqual max
}
...
Finally to add a last layer of abstraction I'm adding a BaseMaxPQSpec that mixes-in the above MaxPQBehaviours and calls its behaviors for three abstract MaxPQ types. Here I'only provide example for Char:
trait BaseMaxPQSpec extends BaseSpec with MaxPQBehaviours {
type CharMaXPQ <: MaxPQ[Char]
def charMaxPQ: CharMaXPQ
val sortedCharsList: List[Char] = List[Char]('C', 'b', 'd', 'a', 'z').sorted
it should behave like nonEmptyMaxPQ(() => charMaxPQ, sortedCharsList)
}
And this is where compiler spits this at me:
[error] ~/Algorithms/Chapter 2 Sorting/algorithms2_1/src/test/scala/ca/vgorcinschi/algorithms2_4/BaseMaxPQSpec.scala:18:25: inferred type arguments [Char,BaseMaxPQSpec.this.CharMaXPQ] do not conform to method nonEmptyMaxPQ's type parameter bounds [T <: Ordered[T],ImplLà <: ca.vgorcinschi.algorithms2_4.MaxPQ[T]]
[error] it should behave like nonEmptyMaxPQ(() => charMaxPQ, sortedCharsList)
[error] ^
[error] ~/Algorithms/Chapter 2 Sorting/algorithms2_1/src/test/scala/ca/vgorcinschi/algorithms2_4/BaseMaxPQSpec.scala:18:42: type mismatch;
[error] found : () => BaseMaxPQSpec.this.CharMaXPQ
[error] required: () => ImplLà
[error] it should behave like nonEmptyMaxPQ(() => charMaxPQ, sortedCharsList)
[error] ^
[error] ~/Algorithms/Chapter 2 Sorting/algorithms2_1/src/test/scala/ca/vgorcinschi/algorithms2_4/BaseMaxPQSpec.scala:18:56: type mismatch;
[error] found : List[Char]
[error] required: List[T]
[error] it should behave like nonEmptyMaxPQ(() => charMaxPQ, sortedCharsList)
[error] ^
[error] three errors found
[error] (Test / compileIncremental) Compilation failed
[error] Total time: 12 s, completed Feb 15, 2020 6:21:35 PM
What is the correct way to set-up my testing framework? Please don't hesitate to ask for details, clarifications.
Given
nonEmptyMaxPQ(() => charMaxPQ, sortedCharsList)
type inference deduces type T = Char because the type of charMaxPQ is MaxPQ[Char], thus
T <: Ordered[T]
becomes Char <: Ordered[Char]
which certainly is not true. Perhaps try specifying Ordering like so
def nonEmptyMaxPQ[T: Ordering, Impl <: MaxPQ[T]](instanceSupplier: () => Impl, sortedInput: List[T])
Note the difference between upper bound T <: Ordered[T] and context bound T: Ordering.
Related
Any idea why this fails to find the implicit view?
// Making B invariant fixes it.
sealed trait ImplicitlyStable[A, +B]
object ImplicitlyStable {
implicit def convertibleToStable[A, B](implicit
aToB: A => B,
// Removing bToB fixes it.
bToB: ImplicitlyStable[B, B]
): ImplicitlyStable[A, B] = ???
}
object Test {
def testConvertibleToStable[A, B](implicit
aToB: A => B,
bToB: ImplicitlyStable[B, B]
): ImplicitlyStable[A, B] =
implicitly[ImplicitlyStable[A, B]]
}
It fails with:
[error] !I e: ImplicitlyStable[A, B]
[error] ImplicitlyStable.convertibleToStable invalid because
[error] !I aToB: A => B
[error] No implicit view available from A => B.
[error]
[error] implicitly[ImplicitlyStable[A, B]]
[error] ^
[error] one error found
It says No implicit view available from A => B which is clearly not true as there is an implicit parameter of that type.
Is this one of those situations where it is inferring Nothing for B? If so, why would it do that?
If I add:
private def unexpected: Nothing = sys.error("Unexpected invocation")
implicit def nothingStableAmbig1[A]: ImplicitlyStable[Nothing, A] = unexpected
implicit def nothingStableAmbig2[A]: ImplicitlyStable[Nothing, A] = unexpected
implicit def nothingStableAmbig3[A]: ImplicitlyStable[A, Nothing] = unexpected
implicit def nothingStableAmbig4[A]: ImplicitlyStable[A, Nothing] = unexpected
Then it fails with:
[error] ambiguous implicit values:
[error] both method nothingStableAmbig3 in object ImplicitlyStable of type [A]ImplicitlyStable[A,Nothing]
[error] and method nothingStableAmbig4 in object ImplicitlyStable of type [A]ImplicitlyStable[A,Nothing]
[error] match expected type ImplicitlyStable[A,B]
[error] implicitly[ImplicitlyStable[A, B]]
[error] ^
So sure enough, it is inferring Nothing, but why?
It seems like this might be a bug in Scala 2 that has been fixed in Scala 3.
I raised an issue for it.
I'm not sure if there is any more reasoning behind why it is inferring Nothing. If anyone knows then that would be great.
I have the following code, that does not get compiled:
final class DbSystemEnvironment[F[_] : MonadError[F, Throwable]] private(env: Environment[F])
extends DbSetting[F] {
override def read(url: String, user: String, pw: String): F[DbParameter] =
(for {
a <- OptionT(env.get(url))
b <- OptionT(env.get(user))
c <- OptionT(env.get(pw))
} yield DbParameter(a, b, c))
.value
.flatMap {
case Some(v) => v.pure[F]
case None => DbSettingError.raiseError[F, DbParameter]
}
}
The compiler complains:
[error] db/DbSystemEnvironment.scala:10:38: cats.MonadError[F,Throwable] does not take type parameters
[error] final class DbSystemEnvironment[F[_] : MonadError[F, Throwable]] private(env: Environment[F])
[error] ^
[error] db/DbSystemEnvironment.scala:16:9: Could not find an instance of Functor for F
[error] c <- OptionT(env.get(pw))
[error] ^
[error] db/DbSystemEnvironment.scala:20:27: value pure is not a member of Any
[error] case Some(v) => v.pure[F]
[error] ^
[error] db/DbSystemEnvironment.scala:21:37: value raiseError is not a member of object io.databaker.db.DbSettingError
[error] case None => DbSettingError.raiseError[F, DbParameter]
It seems, that I do not use MonadError correctly.
The rest of the code:
final case class DbParameter(url: String, user: String, pw: String)
trait Environment[F[_]] {
def get(v: String) : F[Option[String]]
}
object Environment {
def apply[F[_]](implicit ev: Environment[F]): ev.type = ev
def impl[F[_]: Sync]: Environment[F] = new Environment[F] {
override def get(v: String): F[Option[String]] =
Sync[F].delay(sys.env.get(v))
}
}
How to get the code compiled?
The issue here is the constraint syntax. For a type constructor with a single parameter (like Monad), you can write class Foo[F[_]: Monad]. When you need to "partially apply" a type constructor with multiple parameters, like MonadError, the situation is slightly different.
If you're using kind-projector, you can write the following:
class DbSystemEnvironment[F[_]: MonadError[*[_], Throwable]]
This is non-standard syntax, though, and it isn't currently included in Dotty's partial -Ykind-projector compatibility support. I'd recommend just desugaring the implicit parameter list:
class DbSystemEnvironment[F[_]](implicit F: MonadError[F, Throwable]])
This does exactly what you want, doesn't require an extra compiler plugin, and is much more future-proof.
Suppose I have trait that represents something like a polymorphic function, e.g.:
trait Func[A[X, Y]] {
def apply[X, Y](a: A[X, Y]): A[X, Y]
}
Now I want to use my trait as a non-polymorphic function by passing type lambda as argument:
type single[T] = { type t[X, Y] = T }
val aInstance: Func[single[String]#t] =
new Func[single[String]#t] {
def apply[X, Y](a: String): String = ???
}
Now I have method test which does some useful things with func, e.g.
def test[A[X, Y]](f: Func[A]): Unit = ???
And I want to invoke test with aInstance without specifying type parameters by hand:
test(aInstance)
Unfortunately, this code does not compile (but test[single[String]#t](aInstance) does) with error messages:
[error] /test.scala:16:3: no type parameters for method test: (f: Func[A])Unit exist so that it can be applied to arguments (Func[[X, Y]String])
[error] --- because ---
[error] argument expression's type is not compatible with formal parameter type;
[error] found : Func[[X, Y]String]
[error] required: Func[?A]
[error] test(aInstance)
[error] ^
[error] /test.scala:16:8: type mismatch;
[error] found : Func[[X, Y]String]
[error] required: Func[A]
[error] test(aInstance)
[error] ^
[error] two errors found
My question is: how can I modify these declarations to allow compiler to infer all required types automatically?
To those wondering why I declared Func as having [X, Y] but never used them in actual code there is a more real-world and less abstract example:
object GenericTest {
trait Item { def name: String }
class ItemA extends Item { def name: String = "a" }
class ItemB extends Item { def name: String = "b" }
trait MapFn[-A[X <: Item], +B[X <: Item]] {
def apply[X <: Item](data: A[X]): B[X]
}
case class ItemsCollection[C[A <: Item]](a: C[ItemA], b: C[ItemB]) {
def map[D[A <: Item]](f: MapFn[C, D]): ItemsCollection[D] =
ItemsCollection(f(a), f(b))
}
// sometimes we want to store sequences...
val itemSeq = ItemsCollection[Seq](Seq(new ItemA), Seq(new ItemB))
// or transform them:
val itemSet = itemSeq.map(new MapFn[Seq, Set] {
override def apply[X <: Item](data: Seq[X]): Set[X] = data.toSet
})
// but one day we wanted to store just objects without any item-specific types... e.g. names:
type single[X] = { type t[A] = X }
val itemNames = itemSeq.map(new MapFn[Seq, single[String]#t] {
override def apply[X <: Item](data: Seq[X]): String = data.head.name
})
/*
[error] test.scala:28:27: no type parameters for method map: (f: MapFn[Seq,D])ItemsCollection[D] exist so that it can be applied to arguments (MapFn[Seq,[A]String])
[error] --- because ---
[error] argument expression's type is not compatible with formal parameter type;
[error] found : MapFn[Seq,[A]String]
[error] required: MapFn[Seq,?D]
[error] val itemNames = itemSeq.map(new MapFn[Seq, single[String]#t] {
[error] ^
[error] test.scala:28:31: type mismatch;
[error] found : MapFn[Seq,[A]String]
[error] required: MapFn[Seq,D]
[error] val itemNames = itemSeq.map(new MapFn[Seq, single[String]#t] {
[error] ^
[error] two errors found
*/
}
Referring to your GenericTest, there is no way to get Scala to infer that shape, because of this closed-but-unfixed bug.
One thing you can do is try to adapt the technique of Unapply, using implicit resolution to determine the likely candidate for D. This will probably entail defining your own typeclass and instances, not using the ones Scalaz supplies, and possibly changing how MapFn is declared to be more suitable for this pattern. Make sure that the instance that gives you single has the lowest priority, as it can always be used (every T is an F[T] if F = Id).
If you control the definition of MapFn, you may also move the B type parameter to a type member. Then the signature of map becomes
def map(f: MapFn[C]): ItemsCollection[f.B] =
You can add a subclass to MapFn that moves the type member back to a parameter for ease of MapFn creation.
I'm having compilation issues with generics. The code compiles fine when I use asInstanceOf. I want to get rid of asInstanceOf.
I saw some other questions related to the usage of asInstanceOf, but I didn't help me.
trait RoundRobin[R <: Resource, F[_] <: mutable.ListBuffer[_]] {
self: RoundRobin[R, F] =>
// some public functions
private def overrideMutableResourceList(original: F[R], updated: F[R]): F[R] = {
val tempPool = original.asInstanceOf[mutable.ListBuffer[R]]
original.indices.foreach(i => {
val e = updated(i).asInstanceOf[R]
tempPool.update(i, e)
})
tempPool.asInstanceOf[F[R]]
}
When I remove the asInstanceOf from tempPool.asInstanceOf[F[R]] I get the below error
[error] /Users/...../RoundRobin.scala:108: type mismatch;
[error] found : tempPool.type (with underlying type scala.collection.mutable.ListBuffer[R])
[error] required: F[R]
[error] tempPool
[error] ^
[error] one error found
[error] (clustering/compile:compileIncremental) Compilation failed
[error] Total time: 3 s, completed Oct 3, 2017 2:53:34 AM
This issue happens also for the line original.asInstanceOf[mutable.ListBuffer[R]]
What is the reason for this issue?
How can I avoid using asInstanceOf ?
Thanks
There's no relationship between F[A] and ListBuffer[A], only that ∀A∃B F[A] <: ListBuffer[B]. This is important:
type ConstLBInt[A] = ListBuffer[Int]
val x: RoundRobin[Resource, ConstLBInt] = ??? // Legal
// Tries to manipulate ListBuffer[Int]s as if they were ListBuffer[Resources]s
Change the declaration of your type to
trait RoundRobin[R <: Resource, F[A] <: mutable.ListBuffer[A]]
// ! !
This forces ∀A F[A] <: ListBuffer[A], so that e.g. the updated: F[R] in overrideMutableResourceList is known to be a ListBuffer[R].
There are probably other parts of the class that will be simplified by this.
Consider the following, which compiles:
val f: String => Set[Integer] = ???
val a: Set[String] = ???
val b = a.flatMap(s => f(s))
Now, if I change the first line above as following, the code no longer compiles:
val f: String => Set[_ <: Integer] = ???
val a: Set[String] = ???
val b = a.flatMap(s => f(s))
The error is the following:
/Foo.scala:31: error: no type parameters for method flatMap: (f: String => scala.collection.GenTraversableOnce[B])(implicit bf: scala.collection.generic.CanBuildFrom[scala.collection.immutable.Set[String],B,That])That exist so that it can be applied to arguments (String => scala.collection.immutable.Set[_ <: Integer])
[ERROR] --- because ---
[ERROR] argument expression's type is not compatible with formal parameter type;
[ERROR] found : String => scala.collection.immutable.Set[_ <: Integer]
[ERROR] required: String => scala.collection.GenTraversableOnce[?B]
[ERROR] val b = a.flatMap(s => f(s))
[ERROR] ^
[ERROR] /Foo.scala:31: error: type mismatch;
[ERROR] found : String => scala.collection.immutable.Set[_ <: Integer]
[ERROR] required: String => scala.collection.GenTraversableOnce[B]
[ERROR] val b = a.flatMap(s => f(s))
[ERROR] ^
[ERROR] /Foo.scala:31: error: Cannot construct a collection of type That with elements of type B based on a collection of type scala.collection.immutable.Set[String].
[ERROR] val b = a.flatMap(s => f(s))
Why does a compile error result here (I don't understand the above compiler error message), and how should I fix it?
It basically means that Scala doesn't know what type you mean. Let's write something that works:
val b = a.flatMap[Integer, Set[Integer]](s => f(s))
There are two type parameters to flatMap. The second parameter is the final type you'll get. That is, b, above, will be of type Set[Integer]. It could well be declared as Set[_ <: Integer], but that might be confused with the declaration of f, so I decided to make it different in that example.
The first parameter is the type of elements that will go into that collection (that is, the elements that will be produced by s => f(s)). So let's go back to f:
val f: String => Set[_ <: Integer] = ???
The type of element produced is an unknown subtype of Integer -- and, being unknown, Scala can't figure out what to use. Or, in order words, it can't infer the type parameters for flatMap.
By the way, if you want to keep Set[_ <: Integer] all the way, you could do this:
val b = a.flatMap[Integer, Set[_ <: Integer]](s => f(s))
Or even, to obfuscate everything,
val b = a.flatMap[T forSome { type T <: Integer }, Set[_ <: Integer]](s => f(s))