I am trying to switch my code to a component-oriented design.
My point of contention is the following function do() which matches its s argument with some priorily known Strings and calls compute() with the adequate type parameter.
def do(s: String, summoner: Summoner): String = {
s match {
case "a" => summoner.compute[Type_A](outOfScopeVal)
case "b" => summoner.compute[Type_B](outOfScopeVal)
case _ => ""
}
}
I would like to transpose it to a generic trait that can be extended if any new Type_x is required.
[EDIT] It would be a library that external developers can enrich at will, adding a new match between a String identifier and a type.
[EDIT2] I call a library defined like the following:
trait TypeHolder[T <: Type_top] {def score(...): String}
object Converters {
implicit object ConverterOfA extends TypeHolder[Type_A] {
def convertAndMore(...): String = {
/*
compute and return a String
*/
}
}
implicit object ConverterOfB extends TypeHolder[Type_B] {
def convertAndMore(...): String = {
/*
compute and return a String
*/
}
}
}
case class Summoner(...) {
def compute[T <: Type_top](...)(implicit summoner: TypeHolder[T]): String = {
summoner.convertAndMore(...)
}
}
This problem can be reduced to getting a generic tool that returns (some kind of) a type based on an input String.
This question: https://stackoverflow.com/a/23836385/3896166, nears the expected solution but I can not match the requirement of "know[ing] the type of object mapping names ahead of time" as the input String is received dynamically...
Also, Shapeless might be the path to follow, but I merely started going down that path.
Is it even possible?
You could solve this by converting a String to a type (if that were possible), but it is not the only way of solving your underlying problem. (This is therefore an XY question)
Instead, you need to build a Map that takes you from the String to a method that computes the appropriate function. It might work something like this:
def computeFn[T <: Type_top] =
(summoner: Summoner, value: ???) => summoner.compute[T](value)
val computeMap = Map(
"a" -> computeFn[Type_A],
"b" -> computeFn[Type_B]
)
def do(s: String, summoner: Summoner): String =
computeMap(s)(summoner, outOfScopeVal)
It should be straightforward to adapt this so that subclasses can add to the computeMap object in order to define their own mappings.
Related
I have a method definitions like so
def batchCacheable[T: ClassTag](cacheKeys: Seq[CacheKey])(callServiceToFetchValues: Seq[CacheKey] => Try[Map[CacheKey, T]]): Try[Map[CacheKey, T]]
Where CacheKey is a trait defined as having a single method called buildCacheKey, and I have a case class that extends that trait and has a id in it as well.
trait CacheKey {
def buildCacheKey: String
}
case class IDCacheKey(id: String) extends CacheKey {
override def buildCacheKey: String = {
s"CacheKey:$stringId"
}
}
And the function that I want to use for callServiceToFetchValues needs IDCacheKey to get the Id, it looks like this.
private def getStringsFromLMS(cacheKeys: Seq[CacheKey]): Try[Map[CacheKey, String]] = {
cacheKeys.map(_ -> _.Id)
}
So that it returns a map of Keys -> Ids. The problem is that batchCacheable can only pass into it a Seq of CacheKey, but I need it to be a Seq of IDCacheKey for the Id. How can I do this?
A straightforward solution would be to do something like this:
def getStringsFromLMS(cacheKeys: Seq[CacheKey]): Try[Map[CacheKey, String]] = {
cacheKeys.collect { case k: IDCacheKey => k.id }
}
This will silently ignore everything that's not IDCacheKey. If you'd rather throw an error in case there are keys of wrong type in the input, just replace .collect with .map.
Either way, this is not the right solution. The function declared to expect CacheKey should be able to handle any instance of CacheKey, no matter what type.
At a more general level, a CacheKey that doesn't provide enough identity information to fetch the value is not very useful.
Long story short, it looks like your CacheKey trait needs an abstract id method. That would solve your problem in a natural (and "correct") way.
Alternatively, you could further parameterize batchCacheable:
def batchCacheable[T, K <: CacheKey](cacheKeys: Seq[K])(
callServiceToFetchValues: Seq[K] => Try[Map[K, T]]
): Try[Map[K, T]]
This lets you declare getStringsFromLMS to accept IDCacheKey, and still be usable with batchCacheable
I'm trying to make some functions which use both the whole Enumeration and single values chosen from an Enumeration as arguments.
It's easy to make a generic function which accepts any kind of Enumeration object as it's argument, but how can I make a generic function which accepts any single value from an Enumeration as it's argument?
object Xenu extends Enumeration {
type Xenu = Value
val foo, bar, baz = Value
}
object Yenu extends Enumeration {
type Yenu = Value
val blip, blop, blap, blep = Value
}
def doStuff[E <: Enumeration](e:E):Int = {
// Works fine
println("Got an enum: " + e.toString())
e.values.size
}
def funnyBusiness[E <: Enumeration](v: E.Value): Unit = {
// This doesn't work!
println(v)
}
doStuff(Xenu) // Works
doStuff(Yenu) // Works
funnyBusiness(Xenu.bar) // Wrong
funnyBusiness(Yenu.blip) // Wrong
Can you help me write a generic function declaration for "funnyBusiness" such that I can accept any single value from any Enumeration?
There is not much you will be able to achieve with such a signature, but here you go:
The problem in your funnyBusiness is that you use the . notation for the type E, which does not exists. To use a type-dependant type, you use the # notation, such as E#Value.
Apart from this, your function is fine:
def funnyBusiness[E <: Enumeration](v: E#Value): Unit = {
println(v)
}
And the solution turns out to be very easy:
def funnyBusiness[E <: Enumeration](v: E#Value): Unit = {
// This doesn't work!
println(v)
}
I have a very generic message object that I get back from a queue like:
case class Message(key: String, properties: Map[String, String])
I then have a bunch of very specific classes that represent a message, and I use properties.get("type") to determine which particular message it is:
sealed trait BaseMessage
case class LoginMessage(userId: Int, ....) extends BaseMessage
case class RegisterMessage(email: String, firstName: String, ....) extends BaseMessage
Now in my code I have to convert from a generic Message to a particular message in many places, and I want to create this in a single place like:
Currently I am doing something like:
val m = Message(....)
val myMessage = m.properties.get("type") match {
case Some("login") => LoginMessage(m.properties("userID"), ...)
case ...
}
What options do I have in making this less cumbersome in scala?
I don't know all your context here, but I can suggest using implicit conversions if you don't want to bring another library in your project. Anyway, implicit conversions can help you separate a lot the implementation or override it "on-the-fly" as needed.
We can start by defining a MessageConverter trait that is actually a function:
/**
* Try[T] here is useful to track deserialization errors. If you don't need it you can use Option[T] instead.
*/
trait MessageConverter[T <: BaseMessage] extends (Message => Try[T])
Now define an object that holds both the implementations and also enables a nice #as[T] method on Message instances:
object MessageConverters {
/**
* Useful to perform conversions such as:
* {{{
* import MessageConverters._
*
* message.as[LoginMessage]
* message.as[RegisterMessage]
* }}}
*/
implicit class MessageConv(val message: Message) extends AnyVal {
def as[T <: BaseMessage : MessageConverter]: Try[T] =
implicitly[MessageConverter[T]].apply(message)
}
// Define below message converters for each particular type
implicit val loginMessageConverter = new MessageConverter[LoginMessage] {
override def apply(message: Message): Try[LoginMessage] = {
// Parse the properties and build the instance here or fail if you can't.
}
}
}
That's it! It may not be the best solution as implicits bring complexity and they make code harder to follow. However, if you follow a well-defined structure for storing these implicit values and be careful how you pass them around, then you shouldn't have any issues.
You can convert the properties map to Json and read it as a case class. Assuming that the keys to the map have the same name as your case class fields you can write a formatter using playjson:
object LoginMessage {
implicit val fmtLoginMessage = Json.format[LoginMessage]
}
If the fields don't have the same name you will have to specify the reads object manually. Your code to convert it into a case class would be something like:
object BaseMessageFactory {
def getMessage(msg: Message): Option[BaseMessage] = {
val propertiesJson = Json.toJson(msg.properties)
msg.properties.get("type").mapĀ {
case "login" => propertiesJson.as[LoginMessage]
...
case _ => //Some error
}
}
}
The signature may differ depending on how you want to deal with error handling.
I have a situation where I am trying to create a generic function which should be able to take any instance of a class which specifies a certain implicit value in its companion object. I have replicated my problem below:
// Mocking up the library I am working with
trait Formatter[A] {
def output(o: A): String
def input(s: String): A
}
// Some models
trait Human
case class Child(name: String) extends Human
object Child {
implicit val f: Formatter[Child] = new Formatter[Child] {
override def output(c: Child): String = { ... }
override def input(s: String): Child = { ... }
}
}
case class Teen(name: String) extends Human
object Teen {
implicit val f: Formatter[Teen] = new Formatter[Teen] {
override def output(t: Teen): String = { ... }
override def input(s: String): Teen = { ... }
}
}
// The generic function
def gen[A <: Human](a: A)(implicit format: Formatter[A]) = {
// Do something with a formatter...
}
This all works fine, I can pass an instance of a Child or a Teen to my gen function:
gen(Child("Peter"))
gen(Teen("Emily"))
What I am having trouble with is that at run time I only know that the instance I am passing will be a subtype of a Human:
// Example of unknown subtype
val human: Human = Random.nextBoolean match {
case true => Child("Peter")
case false => Teen("Emily")
}
gen(human) // Error: Could not find implicit value for parameter format...
I understand that the error is because Human has no companion object and therefore it has no implementation of a Formatter.
How can I add a constraint to Human that says "anything extending Human will implement a new Formatter" ?
Your scenario fails because you should implement a Formatter[Human]. I think that what you want is that all the Human should be able to have this format "capability" instead.
At this point you have two options, one is to include in the Human trait a method for formatting (this implementation could be in the object if you want it static) or try a dsl approach where you will create a class with the responsibility to provide humans a new capability: "format".
The first approach could be something like this:
trait Human { def format:String }
case class Child(name: String) extends Human {
import Child._
override def format = Child.staticFormat(this)
}
object Child {
def staticFormat(c: Child): String = s"Child(${c.name})"
}
However I think "format" shouldn't be in the contract "Human" so I prefer the second approach:
trait Human
case class Child(name: String) extends Human
case class Teen(name: String) extends Human
import scala.language.implicitConversions
class HumanFormatter(human: Human) {
def format: String = human match {
case c: Child => s"Child(${c.name})"
case t: Teen => s"Teen(${t.name})"
}
}
object HumanDsl {
implicit def humanFormatter(human: Human): HumanFormatter = new HumanFormatter(human)
}
object Test extends App {
def human: Human = Random.nextBoolean match {
case true => Child("Peter")
case false => Teen("Emily")
}
import HumanDsl._
for(i <- 1 to 10) println(human.format)
}
What are the differences between both solutions?
In the first one you force all the new Human classes to implement a format method so you can assure that your code will work always. But at the same time... you are adding a Human a method that from my point of view is not necessary, I think a case class should have only the information needed and if you need to format/parse that class then is better to add this functionality just when needed (dsl approach).
In the other hand, with dsl you should update the formatter anytime a new Human class is created. So it means that the human.format method above will fail if a new Human class is created (you can always match _ to do a default behaviour or raise a custom error).
I think is a matter of design, I hope this would help you a little bit.
Edited:
just like a comment showed the Human trait could be sealed to ensure that the HumanFormatter pattern match doesn't compile if some class is not covered.
You don't need implicits for this.
Just make your subclasses point to the implementation directly:
trait Human[+A <: Human] {
def formatter: Formatter[A]
}
case class Child(name: String) extends Human[Child] {
def formatter = Child.f
}
// etc
def gen[A <: Human](a: A) {
// do something with a.formatter
}
Of course, Formatter needs to be covariant in A too. Otherwise, all bets are off: you simply cannot do what you want - there is nothing useful gen could do with it without knowing the specific type anyway.
If specifics of the concrete type are not needed in gen, you can still use implicits by enumerating them explicitly like this (but I don't really see why you would want that):
object Human {
implicit def formatter(h: Human): Formatter[_] = h match {
case Child(_) => Child.f
case Teen(_) => Teen.f
}
}
gen(h: Human)(implicit f: Formatter[_]) { ... }
Like I said, this does not seem very useful though, so not sure why you want want this over the above approach.
(Essentially I need some kind of a synthesis of these two questions (1, 2), but I'm not smart enough to combine them myself.)
I have a set of JAXB representations in Scala like this:
abstract class Representation {
def marshalToXml(): String = {
val context = JAXBContext.newInstance(this.getClass())
val writer = new StringWriter
context.createMarshaller.marshal(this, writer)
writer.toString()
}
}
class Order extends Representation {
#BeanProperty
var name: String = _
...
}
class Invoice extends Representation { ... }
The problem I have is with my unmarshalling "constructor" methods:
def unmarshalFromJson(marshalledData: String): {{My Representation Subclass}} = {
val mapper = new ObjectMapper()
mapper.getDeserializationConfig().withAnnotationIntrospector(new JaxbAnnotationIntrospector())
mapper.readValue(marshalledData, this.getClass())
}
def unmarshalFromXml(marshalledData: String): {{My Representation Subclass}} = {
val context = JAXBContext.newInstance(this.getClass())
val representation = context.createUnmarshaller().unmarshal(
new StringReader(marshalledData)
).asInstanceOf[{{Type of My Representation Subclass}}]
representation // Return the representation
}
Specifically, I can't figure out how to attach these unmarshalling methods in a typesafe and DRY way to each of my classes, and then to call them from Scala (and hopefully sometimes by using only abstract type information). In other words, I would like to do this:
val newOrder = Order.unmarshalFromJson(someJson)
And more ambitiously:
class Resource[R <: Representation] {
getRepresentation(marshalledData: String): R =
{{R's Singleton}}.unmarshalFromXml(marshalledData)
}
In terms of my particular stumbling blocks:
I can't figure out whether I should define my unmarshalFrom*() constructors once in the Representation class, or in a singleton Representation object - if the latter, I don't see how I can automatically inherit that down through the class hierarchy of Order, Invoice etc.
I can't get this.type (as per this answer) to work as a way of self-typing unmarshalFromJson() - I get a compile error type mismatch; found: ?0 where type ?0 required: Representation.this.type on the readValue() call
I can't figure out how to use the implicit Default[A] pattern (as per this answer) to work down my Representation class hierarchy to call the singleton unmarshalling constructors using type information only
I know this is a bit of a mammoth question touching on various different (but related) issues - any help gratefully received!
Alex
The key is to not try and attach the method to the class but rather pass it in as a parameter. To indicate the type you are expecting and let the type system handle passing it in. I tried to make the unmarshal invocation something that reads a little DSL like.
val order = UnMarshalXml( xml ).toRepresentation[Order]
The following is a fully testable code snippet
abstract class Representation {
def marshalToXml(): String = {
val context = JAXBContext.newInstance(this.getClass)
val writer = new StringWriter
context.createMarshaller.marshal(this, writer)
writer.toString
}
}
#XmlRootElement
class Order extends Representation {
#BeanProperty
var name: String = _
}
case class UnMarshalXml( xml: String ) {
def toRepresentation[T <: Representation](implicit m:Manifest[T]): T = {
JAXBContext.newInstance(m.erasure).createUnmarshaller().unmarshal(
new StringReader(xml)
).asInstanceOf[T]
}
}
object test {
def main( args: Array[String] ) {
val order = new Order
order.name = "my order"
val xml = order.marshalToXml()
println("marshalled: " + xml )
val received = UnMarshalXml( xml ).toRepresentation[Order]
println("received order named: " + received.getName )
}
}
You should see the following output if you run test.main
marshalled: <?xml version="1.0" encoding="UTF-8" standalone="yes"?><order><name>my order</name></order>
received name: my order
Here's the updated version of Neil's code which I used to support the second use case as well as the first:
case class UnmarshalXml(xml: String) {
def toRepresentation[T <: Representation](implicit m: Manifest[T]): T =
toRepresentation[T](m.erasure.asInstanceOf[Class[T]])
def toRepresentation[T <: Representation](typeT: Class[T]): T =
JAXBContext.newInstance(typeT).createUnmarshaller().unmarshal(
new StringReader(xml)
).asInstanceOf[T]
}
This supports simple examples like so:
val order = UnmarshalXml(xml).toRepresentation[Order]
But also for abstract type based usage, you can use like this:
val order = UnmarshalXml(xml).toRepresentation[T](typeOfT)
(Where you have grabbed and stored typeOfT using another implicit Manifest at the point of declaring T.)