Suppose I am testing results of Futures (see below) with specs2.
def f1():Future[String] = {...}
def f2():Future[String] = {...}
I have Matchers[String] to check the results
def m1():Matcher[String] = {...}
def m2():Matcher[String] = {...}
Now I can use implicits to create Matcher[Future[String]].
def fm1():Matcher[Future[String]] = m1.await(retries=2, timeout=2.seconds)
def fm2():Matcher[Future[String]] = m2.await(retries=2, timeout=2.seconds)
So far so good, but I don't like that repeating retries = 2 and timeout = 2.seconds. How can I define them just once and reuse them across all await calls in the spec ?
case class Retry(value:Int) extends AnyVal
case class Timeout(value:Duration) extends Anyval
implicit class MatcherWithImplicitValues[A](m:Matcher[A]) {
def awaitWithImplicitValues(implicit r:Retry, t:Timeout) = {
m.await(retries = r.value, timeout = t.value)
}
}
implicit val retries = Retry(2)
implicit val timeout = Timeout(2 seconds)
m1.awaitWithImplicitValues
Related
I will try to be as clear as possible.
I have this class that combines two other classes HBaseCREvent and HBaseCR:
class HBaseCREvent(val rowKey : scala.Predef.String, val actualEnd : scala.Predef.String, val actualImpact : scala.Predef.String, val actualStart : scala.Predef.String)
class combinedType[T]
object combinedType {
implicit object HBaseCREventWitness extends combinedType[HBaseCREvent]
implicit object HBaseCRWitness extends combinedType[HBaseCR]
}
and I have a method to implement like this
def convertHBaseObj[T:combinedType](event: T) : CRInput = {
event match {
case t # (_: HBaseCREvent | _: HBaseCR) => {
this.setActualEnd(t.actualEnd)
this.setActualImpact(t.actualImpact)
this.setActualStart(t.actualStart)
}
}
}
I am trying to avoid repeating the same set of statements when the input is of type HBaseCREvent or HBaseCR.
However, this approach does not work. My IDE cannot resolve the attributes (t.actualEnd, t.actualImpact, etc).
I have also tried this:
def convertHBaseObj[T:combinedType](event: T) : CRInput = {
event match {
case t: HBaseCREvent|HBaseCR => {
this.setActualEnd(t.actualEnd)
this.setActualImpact(t.actualImpact)
this.setActualStart(t.actualStart)
}
}
}
but I got an error:
illegal variable in pattern alternative
Any help on this?
Thanks
If you have control of HBaseCREvent, HBaseCR then extract your methods into a trait and implement that.
trait MyTrait {
def actualEnd = ???
def actualImpact = ???
def actualEnd = ???
}
def convertHBaseObj(t: MyTrait) = {
setActualEnd(t.actualEnd)
setActualImpact(t.actualImpact)
setActualStart(t.actualStart)
}
If you don't have control then you could do something like the following. I am re-using CombinedEvent here because your version doesn't do anything.
trait CombinedType[T] {
def actualEnd = ???
def actualImpact = ???
def actualEnd = ???
}
implicit def HBaseCREvent2CombinedType(t: HBaseCREvent) = new CombinedType[HBaseCREvent] {
def actualEnd = t.actualEnd
def actualImpact = t.actualImpact
def actualEnd = t.actualEnd
}
implicit def HBaseCR2CombinedType(t: HBaseCR) = new CombinedType[HBaseCR] {
def actualEnd = t.actualEnd
def actualImpact = t.actualImpact
def actualEnd = t.actualEnd
}
def convertHBaseObj[T](event: T)(implicit combinedEvent: CombinedEvent[T]) = {
setActualEnd(combinedEvent.actualEnd)
setActualImpact(combinedEvent.actualImpact)
setActualStart(combinedEvent.actualStart)
}
Either solution is better than your pattern matching implementation because they will fail at compile time rather than at runtime if the wrong type is passed to convertHBaseObj
Running the example code snippet under the subtopic parSequence in Cats Effect document throws an error,
import cats._, cats.data._, cats.syntax.all._, cats.effect.IO
val anIO = IO(1)
val aLotOfIOs = NonEmptyList.of(anIO, anIO)
val ioOfList = aLotOfIOs.parSequence
<console>:44: error: could not find implicit value for parameter P: cats.Parallel[cats.effect.IO,F]
I include implicit Timer[IO] i.e. implicit val timer = IO.timer(ExecutionContext.global) but it does not work. Please advise. Thanks
Update #1
For a complete working snippet,
import cats._, cats.data._, cats.syntax.all._, cats.effect.IO
import scala.concurrent.ExecutionContext.Implicits.global
implicit val contextShift = IO.contextShift(global)
val anIO = IO(1)
val aLotOfIOs = NonEmptyList.of(anIO, anIO)
val ioOfList = aLotOfIOs.parSequence
The implicit you're looking for is defined in cats.effect.IOInstances and you can bring it in scope by importing cats.effect.IO._.
private[effect] abstract class IOInstances extends IOLowPriorityInstances {
//....
implicit def ioParallel(implicit cs: ContextShift[IO]): Parallel[IO, IO.Par] =
new Parallel[IO, IO.Par] {
final override val applicative: Applicative[IO.Par] =
parApplicative(cs)
final override val monad: Monad[IO] =
ioConcurrentEffect(cs)
final override val sequential: ~>[IO.Par, IO] =
new FunctionK[IO.Par, IO] { def apply[A](fa: IO.Par[A]): IO[A] = IO.Par.unwrap(fa) }
final override val parallel: ~>[IO, IO.Par] =
new FunctionK[IO, IO.Par] { def apply[A](fa: IO[A]): IO.Par[A] = IO.Par(fa) }
}
}
object IO extends IOInstances {
// ...
}
Note that you will need to have an implicit ContextShift[IO] in scope if you want to use the ioParallel instance.
It is a common pattern in Scala to have implicit instances defined as part of the companion object for the class (in this case IO).
I should create class Example[T] with 2 different implementations:
If T can be converted to Numeric
Another case
I tried to do this with implicit conversion
class ExampleImplicits{
implicit def convert[T](example: Example[T]): ExampleSecond[T] = new ExampleSecond[T]()
}
class Example[T:Numeric]...
class ExampleSecond[T]...
I thought that if compiler will not find implicit conversion in scala.Numeric.Implicit for T, then he will raise my implicit conversion.
But as I tested code, this works not like this. How can I do this specialization?
UPDATE
What I want to get:
val x = new Example[Int]()
x.methodThatHaveOnlyNumerics()
x.methodThatHaveAllImplementations()
val y = new Example[String]()
y.methodThatHaveAllImplementations()
Now I can't compile my code.
Just create an implicit class that provides the additional methods in the case that the type parameter is Numeric:
class Example[X] {
def methodThatHaveAllImplementations(): Unit = println("all")
}
implicit class ExampleNumOps[N: Numeric](ex: Example[N]) {
def methodThatHaveOnlyNumerics(): Unit = println("num")
}
val x = new Example[Int]()
x.methodThatHaveOnlyNumerics()
x.methodThatHaveAllImplementations()
val y = new Example[String]()
y.methodThatHaveAllImplementations()
Output:
num
all
all
I tested this code. And it seems code working as expected. Here object ob returns ExampleSecond[Int]. I tested with below code.
I hope this is what you expecting.
object HelloWorld {
def main(args: Array[String]): Unit = {
val ex = new Example[Int]
val x = new ExampleImplicits
val ob = x.convert(ex) // passing Example[Int] object to convert
println(ob.isInstanceOf[ExampleSecond[Int]]) // returns true because it is of ExaampleSecond[Int] object.
}
}
class ExampleImplicits {
implicit def convert[T](example: Example[T]): ExampleSecond[T] = new ExampleSecond[T]()
}
class Example[T: Numeric] {
println("ex")
}
class ExampleSecond[T] {
println("ex2")
}
Output :
ex
ex2
true
An alternative to Andrey's answer:
class Example[T] {
def methodThatHaveOnlyNumerics()(implicit num: Numeric[T]) = ...
def methodThatHaveAllImplementations() = ...
}
I have a sum type, Mapping:
sealed trait Mapping
final case class XMapping(a:String)
final case class FallbackMapping(mappings: List[Mapping])
I have a typeclass defined as follows:
final case class Param(x:String)
trait ParameterLoader[T] {
def load(mapping:T) : List[Param]
}
With some instances:
object DefaultParameterLoaders {
implicit val xParameterLoader= new QueryParameterLoader[XMapping] {
override def load(mapping: XMapping): List[Param] = List(Param(mapping.a))
}
implicit val fallbackParameterLoader = new ParameterLoader[FallbackMapping] {
override def load(mapping: FallbackMapping): List[Param] =
mapping.mappings.flatMap(x => ???)
}
}
I can't find a way to have the implicit instances passed to the flatMap above. The error I get is that I'm missing an instance of ParameterLoader[Mapping]. Is there some way of telling the compiler that it should use whatever typeclass instances are in scope?
The type system is looking for a ParameterLoader[Mapping] specifically, meaning that a ParameterLoader[XMapping]/ParameterLoader[FallbackMapping] is not specific enough. You need to provide a ParameterLoader[Mapping]. You can do this using your existing definitions.
implicit def mappingLoader(implicit xpLoader: ParameterLoader[XMapping], fmLoader: ParameterLoader[FallbackMapping]) = new ParameterLoader[Mapping] {
def load(mapping: Mapping): List[QueryParam] =
mapping match {
case xm: XMapping = xpLoader.load(xm)
case fm: FallbackMapping => fmLoader.load(fm)
}
}
Alternatively, have your flatmap perform the matching logic:
implicit def fallbackParameterLoader(implicit xpLoader: ParameterLoader[XMapping]) = new ParameterLoader[FallbackMapping] {
override def load(mapping: FallbackMapping): List[Param] =
mapping.mappings.flatMap {
case xm: XMapping = xpLoader.load(xm)
case fm: FallbackMapping => this.load(fm)
}
}
I'm doing a few operations on Futures inside an trait.
trait MyTrait {
//Future based operations
}
Instead of using ExecutionContext.Implicits.global for my Future, I want to use one that is defined in my application.conf.
akka {
my-batch-dispatcher {
type = Dispatcher
executor = "fork-join-executor"
fork-join-executor {
parallelism-min = 10
parallelism-factor = 2.0
parallelism-max = 10
}
throughput = 20
}
}
Inside my actor I can do a lookup to get the execution context.
implicit val ec = context.system.dispatchers.lookup("akka.my-batch-dispatcher")
Now sure how to do this inside my trait.
You can add it as abstract implicit value of the trait:
trait MyTrait {
implicit val ec: ExecutionContext
//Future based operations
}
Then the code the implement the trait should make sure to provide the ExecutionContext. If that is an Actor you may do something like:
class MyActor extends Actor with MyTrait {
implicit val ec = context.system.dispatchers.lookup("akka.my-batch-dispatcher")
def receive = {
case "hello" => println("hello back at you")
case _ => println("huh?")
}
}
I didn't test it but I think this should work.