Scala Generic Trait Factory










0















In my project I have many events that are very similar. Here's a shortened example:



object Events 
final case class UpdatedCount(id: Int, prevValue: Double, newValue: Double)
extends PropertyEvent[Double]
final case class UpdatedName(id: Int, prevValue: String, newValue: String)
extends PropertyEvent[String]



The trait looks like this:



trait PropertyEvent[A] 
val id: Int
val prevValue: A
val newValue: A



There is a factory that is used to get the appropriate event at runtime. This gets called by another generic method that uses partial functions to get the preValue and newValue:



object PropertyEventFactory
def getEvent[A, B <: PropertyEvent[A]](id: Int, preValue: A, newValue: A, prop: B): PropertyEvent[A]= prop match
case UpdatedCount(_,_,_) => UpdatedCount(id, preValue, newValue)
case UpdatedName(_,_,_) => UpdatedName(id, preValue, newValue)




IntelliJ's intelliSense complains about the preValue and newValue, but the compiler is able to figure it out and build successfully.



Here is a basic spec to show how this might get called:



"Passing UpdatedCount to the factory" should "result in UpdatedCount" in 
val a = PropertyEventFactory.getEvent(0, 1d,2d, UpdatedCount(0,0,0))
assert(a.id == 0)
assert(a.prevValue == 1)
assert(a.newValue == 2)



Is there a way to achieve this by passing UpdatedCount as a type instead of an object? Creating a temporary version of UpdatedCount just to get the actual UpdatedCount Event has code smell to me. I've tried many ways but end up with other issues. Any ideas?



Edit 1:
Added the getEvent calling function and some additional supporting code to help demonstrate the pattern of use.



Here is the basic entity that is being updated. Forgive the use of vars in the case class as it makes the examples much simpler.



final case class BoxContent(id: Int, var name: String, var count: Double, var stringProp2: String, var intProp: Int)


The command used to request an update:



object Commands 
final case class BoxContentUpdateRequest(requestId: Long, entity: BoxContent, fields: Seq[String])



Here is a persistent actor that receives request to update a BoxContent in a Box. The method that calls the factory is in here in the editContentProp function:



class Box extends PersistentActor

override def persistenceId: String = "example"

val contentMap: BoxContentMap = new BoxContentMap()

val receiveCommand: Receive =
case request: BoxContentUpdateRequest =>
val item = request.entity
request.fields.foreach
case "name" => editContentProp(item.id, item.name, contentMap.getNameProp, contentMap.editNameProp, UpdatedName.apply(0,"",""))
case "count" => editContentProp(item.id, item.count, contentMap.getCountProp, contentMap.editCountProp, UpdatedCount.apply(0,0,0))
case "stringProp2" => /*Similar to above*/
case "intProp" => /*Similar to above*/
/*Many more similar cases*/



val receiveRecover: Receive = case _ => /*reload and persist content info here*/


private def editContentProp[A](key: Int, newValue: A, prevGet: Int => A,
editFunc: (Int, A) => Unit, propEvent: PropertyEvent[A]) =
val prevValue = prevGet(key)
persist(PropertyEventFactory.getEvent(key, prevValue, newValue, propEvent)) evt =>
editFunc(key, newValue)
context.system.eventStream.publish(evt)





Edit2:
The suggestion made in the comments to expose a factory method for each event and then pass the factory method seems to be best approach.



Here is the modified Box class:



class Box extends PersistentActor

override def persistenceId: String = "example"

val contentMap: BoxContentMap = new BoxContentMap()

val receiveCommand: Receive =
case request: BoxContentUpdateRequest =>
val item = request.entity
request.fields.foreach
case "name" => editContentProp(item.id, item.name, contentMap.getNameProp, contentMap.editNameProp, PropertyEventFactory.getNameEvent)
case "count" => editContentProp(item.id, item.count, contentMap.getCountProp, contentMap.editCountProp, PropertyEventFactory.getCountEvent)
case "stringProp2" => /*Similar to above*/
case "intProp" => /*Similar to above*/
/*Many more similar cases*/



val receiveRecover: Receive = case _ => /*reload and persist content info here*/

private def editContentProp[A](key: Int, newValue: A, prevGet: Int => A,
editFunc: (Int, A) => Unit, eventFactMethod: (Int, A, A) => PropertyEvent[A]) =
val prevValue = prevGet(key)
persist(eventFactMethod(key, prevValue, newValue)) evt =>
editFunc(key, newValue)
context.system.eventStream.publish(evt)





And here is the modified PropertyEventFactory:



object PropertyEventFactory
def getCountEvent(id: Int, preValue: Double, newValue: Double): UpdatedCount = UpdatedCount(id, preValue, newValue)
def getNameEvent(id: Int, preValue: String, newValue: String): UpdatedName = UpdatedName(id, preValue, newValue)



If one of the commenters who suggested this approach want to propose an answer with this content I'll be happy to upvote it.










share|improve this question
























  • If you know the event type when you call getEvent, why not have a separate factory method for each event and just called the appropriate method?

    – Tim
    Nov 16 '18 at 9:10











  • getEvent gets called by a another generic method, so I doesn't know the event type. It has 2 function arguments (to get the preValue and newValue values) and a PropertyEvent[A] argument that then it passes to getEvent when it makes its request.

    – MORCHARD
    Nov 16 '18 at 14:04











  • I think we need to see the calling function, since that may be where the problem is. But in general you can't use a type parameter to make decisions about how to process data, you need a value for that.

    – Tim
    Nov 16 '18 at 15:03











  • Why your example compiles, baffles me. Can anyone explain ?

    – ygor
    Nov 16 '18 at 15:51











  • Responding to first comment: def getEvent[A, B <: PropertyEvent[A]](id: Int, preValue: A, newValue: A, fact: (Int, A, A) => B): PropertyEvent[A] = fact(id, preValue, newValue) and than call it using PropertyEventFactory3.getEvent(0, 1d, 2d, UpdatedCount.apply)

    – ygor
    Nov 16 '18 at 15:52















0















In my project I have many events that are very similar. Here's a shortened example:



object Events 
final case class UpdatedCount(id: Int, prevValue: Double, newValue: Double)
extends PropertyEvent[Double]
final case class UpdatedName(id: Int, prevValue: String, newValue: String)
extends PropertyEvent[String]



The trait looks like this:



trait PropertyEvent[A] 
val id: Int
val prevValue: A
val newValue: A



There is a factory that is used to get the appropriate event at runtime. This gets called by another generic method that uses partial functions to get the preValue and newValue:



object PropertyEventFactory
def getEvent[A, B <: PropertyEvent[A]](id: Int, preValue: A, newValue: A, prop: B): PropertyEvent[A]= prop match
case UpdatedCount(_,_,_) => UpdatedCount(id, preValue, newValue)
case UpdatedName(_,_,_) => UpdatedName(id, preValue, newValue)




IntelliJ's intelliSense complains about the preValue and newValue, but the compiler is able to figure it out and build successfully.



Here is a basic spec to show how this might get called:



"Passing UpdatedCount to the factory" should "result in UpdatedCount" in 
val a = PropertyEventFactory.getEvent(0, 1d,2d, UpdatedCount(0,0,0))
assert(a.id == 0)
assert(a.prevValue == 1)
assert(a.newValue == 2)



Is there a way to achieve this by passing UpdatedCount as a type instead of an object? Creating a temporary version of UpdatedCount just to get the actual UpdatedCount Event has code smell to me. I've tried many ways but end up with other issues. Any ideas?



Edit 1:
Added the getEvent calling function and some additional supporting code to help demonstrate the pattern of use.



Here is the basic entity that is being updated. Forgive the use of vars in the case class as it makes the examples much simpler.



final case class BoxContent(id: Int, var name: String, var count: Double, var stringProp2: String, var intProp: Int)


The command used to request an update:



object Commands 
final case class BoxContentUpdateRequest(requestId: Long, entity: BoxContent, fields: Seq[String])



Here is a persistent actor that receives request to update a BoxContent in a Box. The method that calls the factory is in here in the editContentProp function:



class Box extends PersistentActor

override def persistenceId: String = "example"

val contentMap: BoxContentMap = new BoxContentMap()

val receiveCommand: Receive =
case request: BoxContentUpdateRequest =>
val item = request.entity
request.fields.foreach
case "name" => editContentProp(item.id, item.name, contentMap.getNameProp, contentMap.editNameProp, UpdatedName.apply(0,"",""))
case "count" => editContentProp(item.id, item.count, contentMap.getCountProp, contentMap.editCountProp, UpdatedCount.apply(0,0,0))
case "stringProp2" => /*Similar to above*/
case "intProp" => /*Similar to above*/
/*Many more similar cases*/



val receiveRecover: Receive = case _ => /*reload and persist content info here*/


private def editContentProp[A](key: Int, newValue: A, prevGet: Int => A,
editFunc: (Int, A) => Unit, propEvent: PropertyEvent[A]) =
val prevValue = prevGet(key)
persist(PropertyEventFactory.getEvent(key, prevValue, newValue, propEvent)) evt =>
editFunc(key, newValue)
context.system.eventStream.publish(evt)





Edit2:
The suggestion made in the comments to expose a factory method for each event and then pass the factory method seems to be best approach.



Here is the modified Box class:



class Box extends PersistentActor

override def persistenceId: String = "example"

val contentMap: BoxContentMap = new BoxContentMap()

val receiveCommand: Receive =
case request: BoxContentUpdateRequest =>
val item = request.entity
request.fields.foreach
case "name" => editContentProp(item.id, item.name, contentMap.getNameProp, contentMap.editNameProp, PropertyEventFactory.getNameEvent)
case "count" => editContentProp(item.id, item.count, contentMap.getCountProp, contentMap.editCountProp, PropertyEventFactory.getCountEvent)
case "stringProp2" => /*Similar to above*/
case "intProp" => /*Similar to above*/
/*Many more similar cases*/



val receiveRecover: Receive = case _ => /*reload and persist content info here*/

private def editContentProp[A](key: Int, newValue: A, prevGet: Int => A,
editFunc: (Int, A) => Unit, eventFactMethod: (Int, A, A) => PropertyEvent[A]) =
val prevValue = prevGet(key)
persist(eventFactMethod(key, prevValue, newValue)) evt =>
editFunc(key, newValue)
context.system.eventStream.publish(evt)





And here is the modified PropertyEventFactory:



object PropertyEventFactory
def getCountEvent(id: Int, preValue: Double, newValue: Double): UpdatedCount = UpdatedCount(id, preValue, newValue)
def getNameEvent(id: Int, preValue: String, newValue: String): UpdatedName = UpdatedName(id, preValue, newValue)



If one of the commenters who suggested this approach want to propose an answer with this content I'll be happy to upvote it.










share|improve this question
























  • If you know the event type when you call getEvent, why not have a separate factory method for each event and just called the appropriate method?

    – Tim
    Nov 16 '18 at 9:10











  • getEvent gets called by a another generic method, so I doesn't know the event type. It has 2 function arguments (to get the preValue and newValue values) and a PropertyEvent[A] argument that then it passes to getEvent when it makes its request.

    – MORCHARD
    Nov 16 '18 at 14:04











  • I think we need to see the calling function, since that may be where the problem is. But in general you can't use a type parameter to make decisions about how to process data, you need a value for that.

    – Tim
    Nov 16 '18 at 15:03











  • Why your example compiles, baffles me. Can anyone explain ?

    – ygor
    Nov 16 '18 at 15:51











  • Responding to first comment: def getEvent[A, B <: PropertyEvent[A]](id: Int, preValue: A, newValue: A, fact: (Int, A, A) => B): PropertyEvent[A] = fact(id, preValue, newValue) and than call it using PropertyEventFactory3.getEvent(0, 1d, 2d, UpdatedCount.apply)

    – ygor
    Nov 16 '18 at 15:52













0












0








0








In my project I have many events that are very similar. Here's a shortened example:



object Events 
final case class UpdatedCount(id: Int, prevValue: Double, newValue: Double)
extends PropertyEvent[Double]
final case class UpdatedName(id: Int, prevValue: String, newValue: String)
extends PropertyEvent[String]



The trait looks like this:



trait PropertyEvent[A] 
val id: Int
val prevValue: A
val newValue: A



There is a factory that is used to get the appropriate event at runtime. This gets called by another generic method that uses partial functions to get the preValue and newValue:



object PropertyEventFactory
def getEvent[A, B <: PropertyEvent[A]](id: Int, preValue: A, newValue: A, prop: B): PropertyEvent[A]= prop match
case UpdatedCount(_,_,_) => UpdatedCount(id, preValue, newValue)
case UpdatedName(_,_,_) => UpdatedName(id, preValue, newValue)




IntelliJ's intelliSense complains about the preValue and newValue, but the compiler is able to figure it out and build successfully.



Here is a basic spec to show how this might get called:



"Passing UpdatedCount to the factory" should "result in UpdatedCount" in 
val a = PropertyEventFactory.getEvent(0, 1d,2d, UpdatedCount(0,0,0))
assert(a.id == 0)
assert(a.prevValue == 1)
assert(a.newValue == 2)



Is there a way to achieve this by passing UpdatedCount as a type instead of an object? Creating a temporary version of UpdatedCount just to get the actual UpdatedCount Event has code smell to me. I've tried many ways but end up with other issues. Any ideas?



Edit 1:
Added the getEvent calling function and some additional supporting code to help demonstrate the pattern of use.



Here is the basic entity that is being updated. Forgive the use of vars in the case class as it makes the examples much simpler.



final case class BoxContent(id: Int, var name: String, var count: Double, var stringProp2: String, var intProp: Int)


The command used to request an update:



object Commands 
final case class BoxContentUpdateRequest(requestId: Long, entity: BoxContent, fields: Seq[String])



Here is a persistent actor that receives request to update a BoxContent in a Box. The method that calls the factory is in here in the editContentProp function:



class Box extends PersistentActor

override def persistenceId: String = "example"

val contentMap: BoxContentMap = new BoxContentMap()

val receiveCommand: Receive =
case request: BoxContentUpdateRequest =>
val item = request.entity
request.fields.foreach
case "name" => editContentProp(item.id, item.name, contentMap.getNameProp, contentMap.editNameProp, UpdatedName.apply(0,"",""))
case "count" => editContentProp(item.id, item.count, contentMap.getCountProp, contentMap.editCountProp, UpdatedCount.apply(0,0,0))
case "stringProp2" => /*Similar to above*/
case "intProp" => /*Similar to above*/
/*Many more similar cases*/



val receiveRecover: Receive = case _ => /*reload and persist content info here*/


private def editContentProp[A](key: Int, newValue: A, prevGet: Int => A,
editFunc: (Int, A) => Unit, propEvent: PropertyEvent[A]) =
val prevValue = prevGet(key)
persist(PropertyEventFactory.getEvent(key, prevValue, newValue, propEvent)) evt =>
editFunc(key, newValue)
context.system.eventStream.publish(evt)





Edit2:
The suggestion made in the comments to expose a factory method for each event and then pass the factory method seems to be best approach.



Here is the modified Box class:



class Box extends PersistentActor

override def persistenceId: String = "example"

val contentMap: BoxContentMap = new BoxContentMap()

val receiveCommand: Receive =
case request: BoxContentUpdateRequest =>
val item = request.entity
request.fields.foreach
case "name" => editContentProp(item.id, item.name, contentMap.getNameProp, contentMap.editNameProp, PropertyEventFactory.getNameEvent)
case "count" => editContentProp(item.id, item.count, contentMap.getCountProp, contentMap.editCountProp, PropertyEventFactory.getCountEvent)
case "stringProp2" => /*Similar to above*/
case "intProp" => /*Similar to above*/
/*Many more similar cases*/



val receiveRecover: Receive = case _ => /*reload and persist content info here*/

private def editContentProp[A](key: Int, newValue: A, prevGet: Int => A,
editFunc: (Int, A) => Unit, eventFactMethod: (Int, A, A) => PropertyEvent[A]) =
val prevValue = prevGet(key)
persist(eventFactMethod(key, prevValue, newValue)) evt =>
editFunc(key, newValue)
context.system.eventStream.publish(evt)





And here is the modified PropertyEventFactory:



object PropertyEventFactory
def getCountEvent(id: Int, preValue: Double, newValue: Double): UpdatedCount = UpdatedCount(id, preValue, newValue)
def getNameEvent(id: Int, preValue: String, newValue: String): UpdatedName = UpdatedName(id, preValue, newValue)



If one of the commenters who suggested this approach want to propose an answer with this content I'll be happy to upvote it.










share|improve this question
















In my project I have many events that are very similar. Here's a shortened example:



object Events 
final case class UpdatedCount(id: Int, prevValue: Double, newValue: Double)
extends PropertyEvent[Double]
final case class UpdatedName(id: Int, prevValue: String, newValue: String)
extends PropertyEvent[String]



The trait looks like this:



trait PropertyEvent[A] 
val id: Int
val prevValue: A
val newValue: A



There is a factory that is used to get the appropriate event at runtime. This gets called by another generic method that uses partial functions to get the preValue and newValue:



object PropertyEventFactory
def getEvent[A, B <: PropertyEvent[A]](id: Int, preValue: A, newValue: A, prop: B): PropertyEvent[A]= prop match
case UpdatedCount(_,_,_) => UpdatedCount(id, preValue, newValue)
case UpdatedName(_,_,_) => UpdatedName(id, preValue, newValue)




IntelliJ's intelliSense complains about the preValue and newValue, but the compiler is able to figure it out and build successfully.



Here is a basic spec to show how this might get called:



"Passing UpdatedCount to the factory" should "result in UpdatedCount" in 
val a = PropertyEventFactory.getEvent(0, 1d,2d, UpdatedCount(0,0,0))
assert(a.id == 0)
assert(a.prevValue == 1)
assert(a.newValue == 2)



Is there a way to achieve this by passing UpdatedCount as a type instead of an object? Creating a temporary version of UpdatedCount just to get the actual UpdatedCount Event has code smell to me. I've tried many ways but end up with other issues. Any ideas?



Edit 1:
Added the getEvent calling function and some additional supporting code to help demonstrate the pattern of use.



Here is the basic entity that is being updated. Forgive the use of vars in the case class as it makes the examples much simpler.



final case class BoxContent(id: Int, var name: String, var count: Double, var stringProp2: String, var intProp: Int)


The command used to request an update:



object Commands 
final case class BoxContentUpdateRequest(requestId: Long, entity: BoxContent, fields: Seq[String])



Here is a persistent actor that receives request to update a BoxContent in a Box. The method that calls the factory is in here in the editContentProp function:



class Box extends PersistentActor

override def persistenceId: String = "example"

val contentMap: BoxContentMap = new BoxContentMap()

val receiveCommand: Receive =
case request: BoxContentUpdateRequest =>
val item = request.entity
request.fields.foreach
case "name" => editContentProp(item.id, item.name, contentMap.getNameProp, contentMap.editNameProp, UpdatedName.apply(0,"",""))
case "count" => editContentProp(item.id, item.count, contentMap.getCountProp, contentMap.editCountProp, UpdatedCount.apply(0,0,0))
case "stringProp2" => /*Similar to above*/
case "intProp" => /*Similar to above*/
/*Many more similar cases*/



val receiveRecover: Receive = case _ => /*reload and persist content info here*/


private def editContentProp[A](key: Int, newValue: A, prevGet: Int => A,
editFunc: (Int, A) => Unit, propEvent: PropertyEvent[A]) =
val prevValue = prevGet(key)
persist(PropertyEventFactory.getEvent(key, prevValue, newValue, propEvent)) evt =>
editFunc(key, newValue)
context.system.eventStream.publish(evt)





Edit2:
The suggestion made in the comments to expose a factory method for each event and then pass the factory method seems to be best approach.



Here is the modified Box class:



class Box extends PersistentActor

override def persistenceId: String = "example"

val contentMap: BoxContentMap = new BoxContentMap()

val receiveCommand: Receive =
case request: BoxContentUpdateRequest =>
val item = request.entity
request.fields.foreach
case "name" => editContentProp(item.id, item.name, contentMap.getNameProp, contentMap.editNameProp, PropertyEventFactory.getNameEvent)
case "count" => editContentProp(item.id, item.count, contentMap.getCountProp, contentMap.editCountProp, PropertyEventFactory.getCountEvent)
case "stringProp2" => /*Similar to above*/
case "intProp" => /*Similar to above*/
/*Many more similar cases*/



val receiveRecover: Receive = case _ => /*reload and persist content info here*/

private def editContentProp[A](key: Int, newValue: A, prevGet: Int => A,
editFunc: (Int, A) => Unit, eventFactMethod: (Int, A, A) => PropertyEvent[A]) =
val prevValue = prevGet(key)
persist(eventFactMethod(key, prevValue, newValue)) evt =>
editFunc(key, newValue)
context.system.eventStream.publish(evt)





And here is the modified PropertyEventFactory:



object PropertyEventFactory
def getCountEvent(id: Int, preValue: Double, newValue: Double): UpdatedCount = UpdatedCount(id, preValue, newValue)
def getNameEvent(id: Int, preValue: String, newValue: String): UpdatedName = UpdatedName(id, preValue, newValue)



If one of the commenters who suggested this approach want to propose an answer with this content I'll be happy to upvote it.







scala generics traits






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 16 '18 at 19:19







MORCHARD

















asked Nov 15 '18 at 20:23









MORCHARDMORCHARD

218211




218211












  • If you know the event type when you call getEvent, why not have a separate factory method for each event and just called the appropriate method?

    – Tim
    Nov 16 '18 at 9:10











  • getEvent gets called by a another generic method, so I doesn't know the event type. It has 2 function arguments (to get the preValue and newValue values) and a PropertyEvent[A] argument that then it passes to getEvent when it makes its request.

    – MORCHARD
    Nov 16 '18 at 14:04











  • I think we need to see the calling function, since that may be where the problem is. But in general you can't use a type parameter to make decisions about how to process data, you need a value for that.

    – Tim
    Nov 16 '18 at 15:03











  • Why your example compiles, baffles me. Can anyone explain ?

    – ygor
    Nov 16 '18 at 15:51











  • Responding to first comment: def getEvent[A, B <: PropertyEvent[A]](id: Int, preValue: A, newValue: A, fact: (Int, A, A) => B): PropertyEvent[A] = fact(id, preValue, newValue) and than call it using PropertyEventFactory3.getEvent(0, 1d, 2d, UpdatedCount.apply)

    – ygor
    Nov 16 '18 at 15:52

















  • If you know the event type when you call getEvent, why not have a separate factory method for each event and just called the appropriate method?

    – Tim
    Nov 16 '18 at 9:10











  • getEvent gets called by a another generic method, so I doesn't know the event type. It has 2 function arguments (to get the preValue and newValue values) and a PropertyEvent[A] argument that then it passes to getEvent when it makes its request.

    – MORCHARD
    Nov 16 '18 at 14:04











  • I think we need to see the calling function, since that may be where the problem is. But in general you can't use a type parameter to make decisions about how to process data, you need a value for that.

    – Tim
    Nov 16 '18 at 15:03











  • Why your example compiles, baffles me. Can anyone explain ?

    – ygor
    Nov 16 '18 at 15:51











  • Responding to first comment: def getEvent[A, B <: PropertyEvent[A]](id: Int, preValue: A, newValue: A, fact: (Int, A, A) => B): PropertyEvent[A] = fact(id, preValue, newValue) and than call it using PropertyEventFactory3.getEvent(0, 1d, 2d, UpdatedCount.apply)

    – ygor
    Nov 16 '18 at 15:52
















If you know the event type when you call getEvent, why not have a separate factory method for each event and just called the appropriate method?

– Tim
Nov 16 '18 at 9:10





If you know the event type when you call getEvent, why not have a separate factory method for each event and just called the appropriate method?

– Tim
Nov 16 '18 at 9:10













getEvent gets called by a another generic method, so I doesn't know the event type. It has 2 function arguments (to get the preValue and newValue values) and a PropertyEvent[A] argument that then it passes to getEvent when it makes its request.

– MORCHARD
Nov 16 '18 at 14:04





getEvent gets called by a another generic method, so I doesn't know the event type. It has 2 function arguments (to get the preValue and newValue values) and a PropertyEvent[A] argument that then it passes to getEvent when it makes its request.

– MORCHARD
Nov 16 '18 at 14:04













I think we need to see the calling function, since that may be where the problem is. But in general you can't use a type parameter to make decisions about how to process data, you need a value for that.

– Tim
Nov 16 '18 at 15:03





I think we need to see the calling function, since that may be where the problem is. But in general you can't use a type parameter to make decisions about how to process data, you need a value for that.

– Tim
Nov 16 '18 at 15:03













Why your example compiles, baffles me. Can anyone explain ?

– ygor
Nov 16 '18 at 15:51





Why your example compiles, baffles me. Can anyone explain ?

– ygor
Nov 16 '18 at 15:51













Responding to first comment: def getEvent[A, B <: PropertyEvent[A]](id: Int, preValue: A, newValue: A, fact: (Int, A, A) => B): PropertyEvent[A] = fact(id, preValue, newValue) and than call it using PropertyEventFactory3.getEvent(0, 1d, 2d, UpdatedCount.apply)

– ygor
Nov 16 '18 at 15:52





Responding to first comment: def getEvent[A, B <: PropertyEvent[A]](id: Int, preValue: A, newValue: A, fact: (Int, A, A) => B): PropertyEvent[A] = fact(id, preValue, newValue) and than call it using PropertyEventFactory3.getEvent(0, 1d, 2d, UpdatedCount.apply)

– ygor
Nov 16 '18 at 15:52












1 Answer
1






active

oldest

votes


















1














This is my attempt to summarize an answer.



First of all, there is no such thing as a generic factory for your trait. Your trait PropertyEvent only specifies three vals, which every subclass of the trait must fulfill after creation. Every class, which implements the trait, can have very different constructors and/or factories.



So, you really need to "enumerate" those factories manually somewhere. Your first attempt works, but it really suffers from code smell and frankly, I am very surprised, that it even compiles. Scala compiler must somehow be able to narrow down the generic A type to a concrete type, once inside a match/case of a case class.



If you try something like this:



object PropertyEventFactory2 
def getEvent[A, B <: PropertyEvent[A]](id: Int, preValue: A, newValue: A, prop: Class[B]): B = prop.getName match
case "org.example.UpdatedCount" => UpdatedCount(id, preValue, newValue)
case "org.example.UpdatedName" => UpdatedName(id, preValue, newValue)




Than this does not compile. You'd need to cast preValue and newValue to the appropriate type and this also is a smelly code.



You could create the event before calling editContentProp:



case "name" => 
val event = UpdatedName(item.id, contentMap.getNameProp(item.id), item.name)
editContentProp(item.id, item.name, contentMap.getNameProp, contentMap.editNameProp, event)



However, all your case branches would repeat the same structure, which is a kind of code duplication. You already recognized it, which is good.



So your best choice really is to pass in a factory for every event. And because all your events are case classes, for every case class you receive a factory method for free, generated by Scala compiler. The factory method resides in the companion object of the case class and it is simply called CaseClass.apply



This leads to the final form of your case branch:



case "name" => editContentProp(item.id, item.name, contentMap.getNameProp, contentMap.editNameProp, UpdatedName.apply)


which is consumed by parameter:



eventFactMethod: (Int, A, A)





share|improve this answer























  • Usage of CaseClass.apply is highlighted in PlayFramework's forms: playframework.com/documentation/2.6.x/ScalaForms

    – ygor
    Nov 16 '18 at 21:05











Your Answer






StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);













draft saved

draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53327361%2fscala-generic-trait-factory%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes









1














This is my attempt to summarize an answer.



First of all, there is no such thing as a generic factory for your trait. Your trait PropertyEvent only specifies three vals, which every subclass of the trait must fulfill after creation. Every class, which implements the trait, can have very different constructors and/or factories.



So, you really need to "enumerate" those factories manually somewhere. Your first attempt works, but it really suffers from code smell and frankly, I am very surprised, that it even compiles. Scala compiler must somehow be able to narrow down the generic A type to a concrete type, once inside a match/case of a case class.



If you try something like this:



object PropertyEventFactory2 
def getEvent[A, B <: PropertyEvent[A]](id: Int, preValue: A, newValue: A, prop: Class[B]): B = prop.getName match
case "org.example.UpdatedCount" => UpdatedCount(id, preValue, newValue)
case "org.example.UpdatedName" => UpdatedName(id, preValue, newValue)




Than this does not compile. You'd need to cast preValue and newValue to the appropriate type and this also is a smelly code.



You could create the event before calling editContentProp:



case "name" => 
val event = UpdatedName(item.id, contentMap.getNameProp(item.id), item.name)
editContentProp(item.id, item.name, contentMap.getNameProp, contentMap.editNameProp, event)



However, all your case branches would repeat the same structure, which is a kind of code duplication. You already recognized it, which is good.



So your best choice really is to pass in a factory for every event. And because all your events are case classes, for every case class you receive a factory method for free, generated by Scala compiler. The factory method resides in the companion object of the case class and it is simply called CaseClass.apply



This leads to the final form of your case branch:



case "name" => editContentProp(item.id, item.name, contentMap.getNameProp, contentMap.editNameProp, UpdatedName.apply)


which is consumed by parameter:



eventFactMethod: (Int, A, A)





share|improve this answer























  • Usage of CaseClass.apply is highlighted in PlayFramework's forms: playframework.com/documentation/2.6.x/ScalaForms

    – ygor
    Nov 16 '18 at 21:05
















1














This is my attempt to summarize an answer.



First of all, there is no such thing as a generic factory for your trait. Your trait PropertyEvent only specifies three vals, which every subclass of the trait must fulfill after creation. Every class, which implements the trait, can have very different constructors and/or factories.



So, you really need to "enumerate" those factories manually somewhere. Your first attempt works, but it really suffers from code smell and frankly, I am very surprised, that it even compiles. Scala compiler must somehow be able to narrow down the generic A type to a concrete type, once inside a match/case of a case class.



If you try something like this:



object PropertyEventFactory2 
def getEvent[A, B <: PropertyEvent[A]](id: Int, preValue: A, newValue: A, prop: Class[B]): B = prop.getName match
case "org.example.UpdatedCount" => UpdatedCount(id, preValue, newValue)
case "org.example.UpdatedName" => UpdatedName(id, preValue, newValue)




Than this does not compile. You'd need to cast preValue and newValue to the appropriate type and this also is a smelly code.



You could create the event before calling editContentProp:



case "name" => 
val event = UpdatedName(item.id, contentMap.getNameProp(item.id), item.name)
editContentProp(item.id, item.name, contentMap.getNameProp, contentMap.editNameProp, event)



However, all your case branches would repeat the same structure, which is a kind of code duplication. You already recognized it, which is good.



So your best choice really is to pass in a factory for every event. And because all your events are case classes, for every case class you receive a factory method for free, generated by Scala compiler. The factory method resides in the companion object of the case class and it is simply called CaseClass.apply



This leads to the final form of your case branch:



case "name" => editContentProp(item.id, item.name, contentMap.getNameProp, contentMap.editNameProp, UpdatedName.apply)


which is consumed by parameter:



eventFactMethod: (Int, A, A)





share|improve this answer























  • Usage of CaseClass.apply is highlighted in PlayFramework's forms: playframework.com/documentation/2.6.x/ScalaForms

    – ygor
    Nov 16 '18 at 21:05














1












1








1







This is my attempt to summarize an answer.



First of all, there is no such thing as a generic factory for your trait. Your trait PropertyEvent only specifies three vals, which every subclass of the trait must fulfill after creation. Every class, which implements the trait, can have very different constructors and/or factories.



So, you really need to "enumerate" those factories manually somewhere. Your first attempt works, but it really suffers from code smell and frankly, I am very surprised, that it even compiles. Scala compiler must somehow be able to narrow down the generic A type to a concrete type, once inside a match/case of a case class.



If you try something like this:



object PropertyEventFactory2 
def getEvent[A, B <: PropertyEvent[A]](id: Int, preValue: A, newValue: A, prop: Class[B]): B = prop.getName match
case "org.example.UpdatedCount" => UpdatedCount(id, preValue, newValue)
case "org.example.UpdatedName" => UpdatedName(id, preValue, newValue)




Than this does not compile. You'd need to cast preValue and newValue to the appropriate type and this also is a smelly code.



You could create the event before calling editContentProp:



case "name" => 
val event = UpdatedName(item.id, contentMap.getNameProp(item.id), item.name)
editContentProp(item.id, item.name, contentMap.getNameProp, contentMap.editNameProp, event)



However, all your case branches would repeat the same structure, which is a kind of code duplication. You already recognized it, which is good.



So your best choice really is to pass in a factory for every event. And because all your events are case classes, for every case class you receive a factory method for free, generated by Scala compiler. The factory method resides in the companion object of the case class and it is simply called CaseClass.apply



This leads to the final form of your case branch:



case "name" => editContentProp(item.id, item.name, contentMap.getNameProp, contentMap.editNameProp, UpdatedName.apply)


which is consumed by parameter:



eventFactMethod: (Int, A, A)





share|improve this answer













This is my attempt to summarize an answer.



First of all, there is no such thing as a generic factory for your trait. Your trait PropertyEvent only specifies three vals, which every subclass of the trait must fulfill after creation. Every class, which implements the trait, can have very different constructors and/or factories.



So, you really need to "enumerate" those factories manually somewhere. Your first attempt works, but it really suffers from code smell and frankly, I am very surprised, that it even compiles. Scala compiler must somehow be able to narrow down the generic A type to a concrete type, once inside a match/case of a case class.



If you try something like this:



object PropertyEventFactory2 
def getEvent[A, B <: PropertyEvent[A]](id: Int, preValue: A, newValue: A, prop: Class[B]): B = prop.getName match
case "org.example.UpdatedCount" => UpdatedCount(id, preValue, newValue)
case "org.example.UpdatedName" => UpdatedName(id, preValue, newValue)




Than this does not compile. You'd need to cast preValue and newValue to the appropriate type and this also is a smelly code.



You could create the event before calling editContentProp:



case "name" => 
val event = UpdatedName(item.id, contentMap.getNameProp(item.id), item.name)
editContentProp(item.id, item.name, contentMap.getNameProp, contentMap.editNameProp, event)



However, all your case branches would repeat the same structure, which is a kind of code duplication. You already recognized it, which is good.



So your best choice really is to pass in a factory for every event. And because all your events are case classes, for every case class you receive a factory method for free, generated by Scala compiler. The factory method resides in the companion object of the case class and it is simply called CaseClass.apply



This leads to the final form of your case branch:



case "name" => editContentProp(item.id, item.name, contentMap.getNameProp, contentMap.editNameProp, UpdatedName.apply)


which is consumed by parameter:



eventFactMethod: (Int, A, A)






share|improve this answer












share|improve this answer



share|improve this answer










answered Nov 16 '18 at 21:03









ygorygor

1,1161615




1,1161615












  • Usage of CaseClass.apply is highlighted in PlayFramework's forms: playframework.com/documentation/2.6.x/ScalaForms

    – ygor
    Nov 16 '18 at 21:05


















  • Usage of CaseClass.apply is highlighted in PlayFramework's forms: playframework.com/documentation/2.6.x/ScalaForms

    – ygor
    Nov 16 '18 at 21:05

















Usage of CaseClass.apply is highlighted in PlayFramework's forms: playframework.com/documentation/2.6.x/ScalaForms

– ygor
Nov 16 '18 at 21:05






Usage of CaseClass.apply is highlighted in PlayFramework's forms: playframework.com/documentation/2.6.x/ScalaForms

– ygor
Nov 16 '18 at 21:05




















draft saved

draft discarded
















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid


  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53327361%2fscala-generic-trait-factory%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Top Tejano songwriter Luis Silva dead of heart attack at 64

政党

天津地下鉄3号線