I have two case class Customer and CustomerResponse and implicit function for conversion from customer to response
case class Customer(name:String)
case class CustomerResponse(customerName:String)
object CustomerImplicits {
implicit def customer2CustomerResponse(value : Customer) =
new CustomerResponse(value.name)
}
Im trying to create a generic function where I can pass lot of classes and perform operation
def getEntityArray[T,U](idArray:Array[String]):Array[U] = {
val records:Array[T] = getRecords[T](idArray)
if(!records.isEmpty) {
val ret = records.map( aRec => aRec:U)
return ret
}
else
return Array.empty[U]
}
arr = getEntityArray[Address,AddressResponse](array)
I'm getting an error T does not conform to expected type U. Is there any way to pass the implicit conversion CustomerImplicits to the getEntityArray function
Consider changing from Array to List and pass implicit conversion implicit ev: T => U argument like so
def getEntityList[T, U](ts: List[T])(implicit ev: T => U): List[U]
For example,
def getEntityList[T, U](ts: List[T])(implicit ev: T => U): List[U] = {
ts.map(ev)
}
import CustomerImplicits._
getEntityList[Customer, CustomerResponse](List(Customer("Picard"), Customer("Worf")))
outputs
res0: List[CustomerResponse] = List(CustomerResponse(Picard), CustomerResponse(Worf))
I think you're trying to achieve this:
import scala.reflect._
def getEntityArray[T, U: ClassTag](idArray: Array[String])(implicit t2u: T => U): Array[U] = {
val records: Array[T] = getRecords[T](idArray)
if(!records.isEmpty)
records.map( r => r: U)
else
Array.empty
}
import CustomerImplicits._
getEntityArray[Customer, CustomerResponse](Array.empty)
However, it doesn't seem to be a good approach for several reasons:
Generic types have no relation with the parameters, that's why you can't make use of type inference.
You are using Array, which is basically a Java type, that's why you need to pass the ClassTag context bound to U for it to work. You can try List instead.
Using implicit conversions this way makes the code less readable. Maybe you can just call it explicitly records.map(t2u), and even make t2u a normal parameter.
Try this:
implicit class Convert[T](req: List[T]) {
import CustomerImplicits._
def gerResp: List[CustomerResponse] = {
req.flatMap {
case customer: Customer => customer :: Nil
case _ => List.empty[CustomerResponse]
}
}
}
val customer = List(Customer("A"), Customer("B"), Customer("C"))
println(customer.gerResp)
in place of asInstanceOf you can do any operation you want which is converting T => U
I think this is what you are looking for.
Related
I want to build a function that takes as input a generic function with one argument,
than parse the argument based on type and call the input function:
class PatternMatching {
val a = "test"
def test[T: TypeTag](callback: T => Unit): Unit = {
callback match {
case x if typeOf[T] <:< typeOf[String] => callback(a)
case x if typeOf[T] <:< typeOf[Array[Char]] => callback(a.toCharArray)
case _ => throw new IllegalArgumentException("error")
}
}
}
I see that type is correctly inferred but is not possible to invoke the function:
type mismatch;
found : PatternMatching.this.a.type (with underlying type String)
required: T
case x if typeOf[T] <:< typeOf[String] => callback(a)
I understand that it's expecting a type, but I can't find a way out.
You need explicit .asInstanceOf[T] with the method you've written:
callback(a.asInstanceOf[T])
Though, what you're trying to achieve is typically done with typeclass in Scala. I let the reader search about it but the general idea is you would have your method defined like this:
def test[T](callback: T => Unit)(implicit converter: ConverterFromString[T]): Unit = {
callback(converter.fromString(a))
}
And there would exist in scope some values of ConverterFromString for only some types you know how to handle.
The huge benefit of this approach is to be type-safe and if will raise errors at compile time rather than runtime if a type cannot be handled.
Using typeclass as suggested by #Gaƫl J works:
trait Converter[A] {
def conveterFromString(a: String): A
}
object Converter {
implicit val stringConverter: Converter[String] = new Converter[String] {
def conveterFromString(x: String): String = x
}
implicit val arrayConverter: Converter[Array[Char]] = new Converter[Array[Char]] {
def conveterFromString(x: String): Array[Char] = x.toCharArray
}
}
class PatternMatching {
val a = "test"
def test[A](callback: A => Unit)(implicit converter: Converter[A]): Unit =
callback(converter.conveterFromString(a))
}
Problem: Chaining multiple Either returning functions, the Left of which are all failures inheriting from a common sealed trait InternalError. However, the compiler complains that the chain is returning Either[_,Success] instead of Either[InternalError, Success].
Here's the code that does the chaining:
import scala.language.implicitConversions
object EitherExtension {
implicit class AndThenEither[A,B](val e: Function1[A,Either[_,B]]) {
//get ability to chain/compose functions that return aligning Eithers
def andThenE[C](f:Function1[B, Either[_,C]]): Function1[A, Either[_,C]] = {
(v1: A) => e.apply(v1).flatMap(b => f.apply(b))
}
}
}
As was pointed out in the comments this discards the type of Left. If I change it the below it will not work since the final output can be of type Either[X|Y, C] which resolves to Either[_,C] and I'm back to square one.
implicit class AndThenEither[A,B,X](val e: (A) => Either[X, B]) {
def andThenE[C,Y](f:(B) => Either[Y, C]): (A) => Either[_, C] = {
(v1: A) => e.apply(v1).flatMap(b => f.apply(b))
}
}
Here's the example showing the compositional failure of type alignment:
import EitherExtension._
object AndThenComposition {
//sample type definitions of failure responses
sealed trait InternalError
case class Failure1() extends InternalError
case class Failure2() extends InternalError
//sample type definitions
case class Id(id: Int)
case class Stuff()
//sample type definitions of successful responses
case class Output1()
case class Output2()
case class InputRequest()
val function1: (InputRequest) => Either[Failure1, Output1] = ???
val function2: (Output1) => Either[Failure2, Output2] = ???
def doSomething(s:Id, l:List[Stuff]): Either[InternalError, Output2] = {
val pipeline = function1 andThenE function2
pipeline(InputRequest()) //Why is this of type Either[_, Output2]
}
}
What am I missing? How can I get the return type to not be Either[Any, Output2] but rather the base/sealed trait? Is this possible to do generically?
You need to preserve the type of the left so we will modify the extension method to do that.
Note that, since both eithers can have different left types, what we will do is use a type bound to ask the compiler to infer the LUB between those types; thanks to Any this is always possibles (although not always helpful).
object EitherExtension {
implicit class AndThenEither[I, L1, R1](private val f: I => Either[L1, R1]) extends AnyVal {
def andThenE[L2 >: L1, R2](g: R1 => Either[L2, R2]): I => Either[L2, R2] =
i => f(i).flatMap(g)
}
}
Which can be used like this:
import EitherExtension._
object AndThenComposition {
sealed trait InternalError
final case object Failure1 extends InternalError
final case object Failure2 extends InternalError
val function1: Int => Either[Failure1.type, String] = ???
val function2: String => Either[Failure2.type, Boolean] = ???
def doSomething(input: Int): Either[InternalError, Boolean] = {
(function1 andThenE function2)(input)
}
}
See the code running here.
In case you're using this in production, and it's not just a learning thing, what you're looking for it's called Kleisli, and fortunately cats-core already implements it.
According to the cats-core docs:
Kleisli enables composition of functions that return a monadic value,
for instance an Option[Int] or a Either[String, List[Double]], without
having functions take an Option or Either as a parameter, which can be
strange and unwieldy.
Since Kleisli composes two functions with the signature A => F[B], you'd need only one abstraction to be able to use Kleisli, which is creating a new type for your operation:
type Operation[A] = Either[InternalFailure, A]
By doing this, you should be able to use Kleisli like this:
import cats.data.Kleisli
val first: Kleisli[Operation, InputRequest, Output1] = Kleisli { request: InputRequest =>
Left(Failure1())
}
val second: Kleisli[Operation, Output1, Output2] = Kleisli { output: Output1 =>
Right(Output2())
}
val composed = first.andThen(second)
I'm trying to write a generic implicit instance for the following structure :
object SmartHasher {
trait GetSmartHash[T] {
def calculateHash(value: T): Int
}
object syntax {
implicit class GetHashOps[T: GetSmartHash](itemToBeHashed: T) {
def hashItSmartway(implicit GetSmartHashInstance: GetSmartHash[T]): Int =
GetSmartHashInstance.calculateHash(itemToBeHashed)
}
}
object instances {
import syntax._
implicit def stringSmartHash: GetSmartHash[String] = (s: String) =>s.size % 10 // smart demo thing :D
}
Now I'm trying to define a generic implicit instance that works for any Vector, List, Or Array
where smarthash method for its elements are defined
:
What type I shoud use instead of Seq , as Im trying with it and it doesnt work out ?
implicit def sequenceHash[T: GetSmartHash]: GetSmartHash[Seq[T]] =
(sequence: Seq[T]) => sequence.map(element => element.hashItSmartway).product
If I define It for an exact collection like Vector :
implicit def sequenceHash[T: GetSmartHash]: GetSmartHash[Vector[T]] =
(sequence: Vector[T]) => sequence.map(element => element.hashItSmartway).product
then it works as I expected.
Any suggestions to write this in generic way for theese 3 collections, to prevent for repeating simillar code 3 times?
Regards.
Try
object instances {
import syntax._
// stringSmartHash ...
implicit def sequenceHash[Col[X] <: Iterable[X], T: GetSmartHash]: GetSmartHash[Col[T]] =
(sequence: Col[T]) => sequence.map(element => element.hashItSmartway).product
}
Testing:
import instances._, syntax._
List("aa", "bbb").hashItSmartway //6
Seq("aa", "bbb").hashItSmartway //6
Vector("aa", "bbb").hashItSmartway //6
If you want sequenceHash to work also for Array replace upper bound with view bound
implicit def sequenceHash[Col[_], T: GetSmartHash](implicit ev: Col[T] => Iterable[T]): GetSmartHash[Col[T]] =
(sequence: Col[T]) => sequence.map(element => element.hashItSmartway).product
Let's say I have a trait like:
trait MyTrait[T, U <: SomeParentClass] {
def get(data: T): Option[U]
}
and a concrete implementation like:
case class MyStringClass[U <: SomeParentClass](f: String => Option[U])
extends MyTrait[String, U] {
override def get(data: String) = f(data)
}
To simplify things, let's also say we have the following types for U <: SomeParentClass:
TypeA
TypeB
TypeC
and some functions:
def str2TypeA(s: String): Option[TypeA] = ...
def str2TypeB(s: String): Option[TypeB] = ...
def str2TypeC(s: String): Option[TypeC] = ...
Then let's say I have:
val mySeq = Seq(
MyStringClass(str2TypeA),
MyStringClass(str2TypeB),
MyStringClass(str2TypeC)
)
What I want to do is filter mySeq based on the return type U. Something like:
mySeq.collect { case a: Function1[_, Option[TypeA]] => a}
I'm running into type erasure issues as expected. I'm curious what approaches might work well here to achieve my goal of filtering based on the type U.
You would usually use a TypeTag to handle cases where erasure gets in your way. They are essentially simple objects that contain all of the information available at compile-time, and they're easy to obtain either by requesting an implicit parameter, or just writing typeTag[T]. For example:
import scala.reflect.runtime.universe._
case class MyStringClass[U <: SomeParentClass](f: String => Option[U])(implicit val utt: TypeTag[U])
extends MyTrait[String, U] {
override def get(data: String) = f(data)
}
mySeq.collect {
case a: MyStringClass[Option[TypeA]] if (a.utt == typeTag[Option[Type[A]]) => a
}
I am trying to implement a ReadJsonCodec of sorts using the automatic type class derivation mechanism in Shapeless.
Here is my ReadCodecCompanionObject:
object ReadCodec extends LabelledProductTypeClassCompanion[ReadCodec] {
implicit object StringCodec extends SimpleCodec[String] {
def read(j: Json): String = j.stringOr(throw ...)
}
implicit object IntCodec ...
implicit object BooleanCodec ...
implicit object LongCodec ...
implicit object ShortCodec ...
implicit object DoubleCodec ...
implicit object BigDecimalCodec ...
implicit def readCodecInstance: LabelledProductTypeClass[ReadCodec] = new LabelledProductTypeClass[ReadCodec] {
def emptyProduct = new ReadCodec[HNil] {
// This will silently accept extra fields within a JsonObject
// To change this behavior make sure json is a JsonObject and that it is empty
def read(json: Json) = HNil
}
def product[F, T <: HList](name: String, FHead: ReadCodec[F], FTail: ReadCodec[T]) = new ReadCodec[F :: T] {
def read(json: Json): F :: T = {
val map = castOrThrow(json)
val fieldValue = map.getOrElse(name, throw new MappingException(s"Expected field $name on JsonObject $map"))
// Try reading the value of the field
// If we get a mapping exception, intercept it and add the name of this field to the path
// If we get another exception, don't touch!
// Pitfall: if handle did not accept a PartialFunction, we could transform an unknow exception into a match exception
val head: F = Try(FHead.read(fieldValue)).handle{ case MappingException(msg, path) => throw MappingException(msg, s"$name/$path")}.get
val tail = FTail.read(json)
head :: tail
}
}
def product[A, T <: HList](name: String, FHead: ReadCodec[Option[A]], FTail: ReadCodec[T]) = new ReadCodec[Option[A] :: T] {
def read(json: Json): Option[A] :: T = {
val map = castOrThrow(json)
val head: Option[A] = map.get(name).map { fieldValue =>
Try(FHead.read(fieldValue)).handle{ case MappingException(msg, path) => throw MappingException(msg, s"$name/$path")}.get.get
}
val tail = FTail.read(json)
head :: tail
}
}
def project[F, G](instance: => ReadCodec[G], to : F => G, from : G => F) = new ReadCodec[F] {
def read(json: Json): F = from(instance.read(json))
}
}
}
That's somewhat of a complicated piece of code to just grasp quickly but is really fairly simple once you understand it. The important part is the two def product methods. The problem I am having is that I want this codec to accept a Json AST that is missing a field if that field would be mapped to a value of type Option[A]. This means that I need the product function to know if the head of the HList is of type Option[A] or not.
Concretely:
case class Foo(a: String, b: Option[Boolean])
val test = read[Foo](json"""{"a" : "Beaver"}""") // should work
But currently this would fail because it makes no distinction for Option and expects a field b. I tried two ways of fixing this and neither of them worked.
The first one and the cleanest one is to overload the product method with a version where the F type parameter is replaced with Option[A]. This approach is fairly clean, although I am not sure whether or not it would play well with the shapeless derivation macro. However, it is not possible because Scala does not support the ability to overload functions when the type signature after erasure is the same. This is the version that is above. Unfortunately, this does not currently compile with the Scala compiler.
The second approach was to use a TypeTag to find out at runtime if F is of type Option[_] and behave appropriately. This version would almost certainly be less elegant and involve a cast but I could live with it. However, it seems impossible because the addition of a TypeTag changes the signature of the product method (adding an implicit parameter) and then the compiler complains that I am not defining the abstract method product.
Does anyone have any suggestions on the best way to proceed.
You need to get a typeclass with information about F passed through. But you're already passing a typeclass around, in the form of ReadCodec. So the solution is to replace that with one that contains all the information you need:
trait ReadCodecAndTypeTag[A] {
val rc: ReadCodec[A]
val tt: TypeTag[A]
}
But in that case you might as well delegate the decoding-from-optional-value-in-a-map to this typeclass as well:
trait OReadCodec[A] {
val rc: ReadCodec[A]
def missingField(name: String, map: Any): A =
throw new MappingException(s"Expected field $name on JsonObject $map")
}
implicit object StringCodec extends OReadCodec[String] {
val rc = new ReadCodec[String] {...}
}
implicit object IntCodec ...
...
implicit def OptionCodec[A](implicit orc: OReadCodec[A]) =
new OReadCodec[Option[A]] {
val rc = ...
override def missingField(name: String, map: Any) = None
}
...
def product[F, T <: HList](name: String, FHead: OReadCodec[F], FTail: OReadCodec[T]) =
new OReadCodec[F :: T] {
val rc = new ReadCodec[F :: T] {
def read(json: Json): F :: T = {
val map = castOrThrow(json)
val fieldValue = map.getOrElse(name, FHead.missingField(name, map))
val head: F = ...
...
}
}
}
implicit def ReadCodecFromOReadCodec[A](implicit orc: OReadCodec[A]) = orc.rc