Here is a strawman program which illustrates the issue I am having
trait RequestBuilder {
type Out
def complete(p: Promise[Out]): Unit
}
def makeRequest(in: RequestBuilder): Source[(RequestBuilder, Promise[in.Out]), Future[in.Out]] = {
val p = Promise[in.Out]
Source.single(in -> p).mapMaterializedValue(_ => p.future)
}
val sink = MergeHub.source[(RequestBuilder, Promise[???])].to(Sink.foreach {
case (r, p) => r.complete(p)
}).run()
sink.runWith(makeRequest(new RequestBuilder {
type Out = Int
def complete(p: Promise[Out]): Unit = p.success(1)
}))
The issue is, how do I type the Promise[???] in the sink? I have been able to work around this by making the Promise a part of the RequestBuilder trait itself, but this seems like a code smell to me
You can make a type alias for the tuple:
type BuilderWithPromise[B <: RequestBuilder] = (B, Promise[B#Out])
Or a case class:
case class BuilderWithPromise[B <: RequestBuilder)(
builder: B,
promise: Promise[B#Out]
)
either way should work
Related
I have a trait with common parameters for a data source, with case classes for each actual source
trait AbstractSource {
val name: String
}
case class ConcreteSource(name: String) extends AbstractSource
I also have a trait for a class which acts on this data source (contravariant for the source)
trait AbstractConsumer[-T <: AbstractSource] {
def foo(inp: T): Unit
}
class ConcreteConsumer extends AbstractConsumer[ConcreteSource] {
override def foo(inp: ConcreteSource): Unit =
println(inp.name)
}
I want to create a factory method for my pipeline method which creates the correct consumer based on the input data source. I have tried the following but both have errors
object ConsumerFactory {
def create(inp: AbstractSource): AbstractConsumer[_ <: AbstractSource] =
inp match {
case _: ConcreteSource => new ConcreteConsumer()
case _ => ???
}
def createTwo[T <: AbstractSource](inp: T): AbstractConsumer[T] =
inp match {
case _: ConcreteSource => new ConcreteConsumer() // errors "required: AbstractConsumer[T], found: ConcreteConsumer"
case _ => ???
}
}
class Main {
def pipeline[T <: AbstractSource](consumer: AbstractConsumer[T], source: T): Unit =
consumer.foo(source)
def execute(): Unit = {
val consumer: ConcreteSource = ConcreteSource("john")
val source = ConsumerFactory.create(consumer) // errors "found: AbstractConsumer[_$1] where type _$1 <: AbstractSource, required: AbstractConsumer[ConcreteSource]"
val source = ConsumerFactory.createTwo(consumer)
pipeline(source, consumer)
}
}
val x = new Main()
x.execute()
If I understand correctly, the issue is that I need to supply a subtype of AbstractConsumer[T] to the pipeline, but I don't know how to do this based on the input due to the contravariant type parameter.
IMHO, theese kinds of problems are more easily solvable with a typeclass, like this:
trait AbstractConsumer[-S <: AbstractSource] {
def consume(inp: S): Unit
}
object AbstractConsumer {
sealed trait ConsumerFactory[S <: AbstractSource] {
type Consumer <: AbstractConsumer[S]
def createConsumer(): Consumer
}
type Factory[S <: AbstractSource, C <: AbstractConsumer[S]] = ConsumerFactory[S] { type Consumer = C }
object Factory {
def apply[S <: AbstractSource, C <: AbstractConsumer[S]](factory: => C): Factory[S, C] =
new ConsumerFactory[S] {
override final type Consumer = C
override final def createConsumer(): Consumer =
factory
}
}
// Get by type.
def apply[S <: AbstractSource](implicit factory: ConsumerFactory[S]): factory.Consumer =
factory.createConsumer()
// Get by value.
def fromSource[S <: AbstractSource](source: S)(implicit factory: ConsumerFactory[S]): factory.Consumer =
factory.createConsumer()
}
Then the concrete source will implement the typeclass, like this:
final class ConcreteConsumer extends AbstractConsumer[ConcreteSource] {
override def consume(inp: ConcreteSource): Unit =
println(inp.name)
}
object ConcreteConsumer {
implicit final val ConcreteConsumerFactory: AbstractConsumer.Factory[ConcreteSource, ConcreteConsumer] =
AbstractConsumer.Factory(new ConcreteConsumer())
}
And, finally, you can use it like this:
import ConcreteConsumer._ // Put the factory in scope.
val source = new ConcreteSource("john")
val consumer1 = AbstractConsumer[ConcreteSource]
val consumer2 = AbstractConsumer.fromSource(source)
You may adapt the code if the factory needs some arguments or something.
The code can be seen running here.
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.
I am looking for an elegant way to chain partial functions that derive from a common base type. The idea is that each partial function handles a type so they become easy to compose for different types and have a common catch-all if the chained partial function is undefined:
trait Message
trait SysMessage extends Message
trait UserMessage extends Message
case class TextSysMessage(m: String) extends SysMessage
case class TextUserMessage(m: String) extends UserMessage
class Test {
type MessagePF = scala.PartialFunction[Message, String]
type SysMessagePF = scala.PartialFunction[SysMessage, String]
type UserMessagePF = scala.PartialFunction[UserMessage, String]
def getSysMessage: SysMessagePF = {
case sm: TextSysMessage ⇒ s"System message: ${sm.m}"
}
def getUserMessage: UserMessagePF = {
case um: TextUserMessage ⇒ s"User message: ${um.m}"
}
def * : MessagePF = {
case m ⇒ s"Unknown message: $m"
}
// Chained partials fails because `m` is a SysMessage with UserMessage
def handler(m: Message): String = (getSysMessage orElse getUserMessage orElse *)(m)
}
Clearly, this approach does not compile. I can get around this by nested pattern matching like this
def getSysMessage: MessagePF = {
case m: SysMessage ⇒ m match {
case sm: TextSysMessage ⇒ s"System message: ${sm.m}"
}
}
but then I loose the capability of handling unknown messages in a catch all. Is there some elegant way to achieve this goal?
As addition to #adamwy + Hongxu Chen's answer, you can define your very own combinator, which involves implicit parameters, so enforces slightly different application syntax
implicit class PartFuncOps[A: ClassTag, B](pf: PartialFunction[A, B]) {
def or[D >: A, C <: D : ClassTag](other: PartialFunction[C, B]): PartialFunction[D, B] = {
case a: A if pf.isDefinedAt(a) ⇒ pf(a)
case c: C if other.isDefinedAt(c) ⇒ other(c)
}
}
Now you can write
def combine = getSysMessage or getUserMessage or *
def handler(m: Message): String = combine(m)
Or
def handler(m: Message): String = (getSysMessage or getUserMessage or *).apply(m)
As suggested by #adamwy, you can change the partial function type to be:
type MessagePF = scala.PartialFunction[Message, String]
type SysMessagePF = scala.PartialFunction[Message, String]
type UserMessagePF = scala.PartialFunction[Message, String]
I have the base class Message and two derived classes One and Two. Method regHandler accepts function Message => Message as argument. When i created method handler and passed it to regHandler, it throws cast error.
class Message
class One extends Message
class Two extends Message
type Handler = Message => Message
def regHandler(handler: Handler) {}
def handler(one: One) : Two = {
new Two
}
regHandler(handler) // <- error here
I tried change my code like this:
var f : Handler
def regHandler[T >: Message, R >: Message](handler: T => R) = {
f = handler // <- but cast error here occurred
}
How do i need change my code to make it work ?
There is a very good reason it doesn't work: your Handler definition says it accepts any Message, but handler only accepts One. Kolberg proposes one solution, but the simpler one would be just to fix handler:
def handler(m: Message) = m match {
case one: One => new Two
case _ => // whatever you want to do here
}
You can throw an exception, which would be equivalent to other answers, except you can have a better error message; you can remove the line, which will throw MatchError; you can return Option[Message]; etc.
Now you can assign it, store it in a map, etc. easily:
object Main extends App {
class Message
class One extends Message
class Two extends Message
type Handler = Message => Message
def regHandler(handler: Handler) {}
def handler(m: Message) = m match {
case one: One => new Two
case _ => throw new Exception(s"handler expected One, got $m")
}
regHandler(handler)
val f: Handler = handler // no _ required
val map: Map[String, Handler] = Map("1" -> handler)
}
Or if you really don't want to change method signatures, make Handlers out of them explicitly (it could be an implicit conversion as well, but since it can introduce errors, I don't recommend it):
object Handler {
def apply[A <: Message, B <: Message](f: A => B): Handler = f.asInstanceOf[Handler]
// alternately if you want to handle ClassCastException for wrong argument type
// def apply[A <: Message, B <: Message](f: A => B): Handler =
// x => try { f(x) } catch { case e: ClassCastException => ... }
}
regHandler(Handler(handler))
The type bounds in
def regHandler[T >: Message, R >: Message](handler: T => R) = {
are wrong, because with >: you are telling the compiler that T,R should be super types of Message. I think you want sub types of Message:
def regHandler[T <: Message, T <: Message](handler: T => R) {}
def handler(one: One) : Two = {
new Two
}
regHandler(handler)
works for me.
EDIT:
OK, my answer was incomplete. Here is a complete and working example:
class Message
class One extends Message
class Two extends Message
type Handler[A <: Message, B <: Message] = A => B
def regHandler[A <: Message, B <: Message](a: A)(handler: Handler[A, B]): Unit = {
println(s"${handler(a)}")
}
def handler(one: One) : Two = {
new Two
}
regHandler(new One)(handler)
EDIT 2:
For usage in a map function or to assign to a var/val in this specific scenario add _:
val f: Handler[_, _] = handler _
val map = Map(new One -> handler _)
val test = map.map {case (one, h) => h(one)}
You can use variance annotation with wildcard types. Check this:
object Main {
class Message
class One extends Message
class Two extends Message
class Three extends Message
class Four extends Message
type HandlerType[+A <: Message, +B <: Message] = A => B
var f: HandlerType[_, _] = _
def regHandler(handler: HandlerType[_, _]) = {
f = handler // <- but cast error here occurred
}
def main(args: Array[String]) {
def handler12(x: One): Two = new Two
def handler23(x: Two): Three = new Three
def handler34(x: Three): Four = new Four
val h12 = handler12(_)
val h23 = handler23(_)
val h34 = handler34(_)
val handlersMap: Map[String, HandlerType[_, _]] = Map(
"12" -> h12,
"23" -> h23,
"34" -> h34)
handlersMap foreach {
case (k, h) =>
println(k)
println(h)
val hd: HandlerType[Message, Message] = h
val m: Message = new One
println(hd(m))
}
regHandler(h12)
}
}
Although you can store the handlers into a map, it creates additional types which introduce more complexity. I think there should be a better way to handle this problem.
I am trying to work around a type erasure in pattern matching. Assuming:
import java.io._
trait Serializer[V] {
def save(os: OutputStream, v: V): Unit
def load(in: InputStream): V
}
trait HasSerializer[V] { def serializer: Serializer[V] }
How can I get this to compile without warning and without asInstanceOf:
def test[V](os: OutputStream, v: V): Unit = v match {
case hs: HasSerializer[V] => hs.serializer.save(os, v)
case _ => ???
}
? test is called with a value from a map and there is no means to provide a class manifest.
Any fancy extractor trick maybe?
If you can make Serializer an abstract class instead, you can give it a Manifest as an implicit constructor parameter, and use that to get the concrete class at construction, then use it later for dynamic type checks.
import java.io._
abstract class Serializer[V: Manifest] {
def save(os: OutputStream, v: V): Unit
def load(in: InputStream): V
val clazz = manifest[V].erasure
}
val ser = new Serializer[Int] {
def save(os: OutputStream, v: Int) {
os.write((v.toString + "\n").getBytes)
}
def load(in: InputStream) = {
val line = new BufferedReader(new InputStreamReader(in)).readLine()
line.toInt
}
}
ser.clazz // java.lang.Class[_] = int
Well the question has kind of a wrong precondition (as I just realize) -- we can take Serializer apart into a serializer and a deserializer. Obviously, when I have an instance of V, my use case is serialization, and that doesn't require V as a return type. Thus
trait Serializer { def save(os: OutputStream): Unit }
would suffice, and any type can mix that in. And do:
def testSer[V](os: OutputStream, v: V): Unit = v match {
case s: Serializer => s.save(os)
case _ => new ObjectOutputStream(os).writeObject(v)
}
And for deserialization, we would either provide the deserializer along with the construction of a Ref[V], or rely on class lookup through ObjectInputStream.