Open rafabap opened 7 years ago
@rafabap I don't think that you want the item to carry around all the necessary to value it. In particular for market prices, I think it would be much better to use some kind of publish/subscribe pattern where the market publishes prices (not unlike a stock ticker) and agents subscribe to market price tickers in those markets in which they trade.
I also don't like the idea that the valuation function should take the the agent as an argument. What type signature did you have in mind for a valuation function? Here is one possibility (apologies for the Scala but there is surely a Java 8 equivalent)...
trait Value extends AnyVal
trait ValuationFunction[-T <: Tradable, +V <: Value] extends (T => V) {
def apply(tradable: T): V
}
case class Price(value: Long) extends Value
case class MarketValuationFunction[-T <: Tradable](market: Price) extends ValuationFunction[T, Price] {
def apply(tradable: T): Price = market
}
...idea would be that an when an agent needs to value a particular tradable item, it would lookup the market price for the item and then use it to create an instance of the MarketValuationFunction
class which it then passes off to the data structure that will apply the function to a particular item instance to produce the value.
@bherd-rb what are your thoughts?
@rafabap @davidrpugh I agree with David. The valuation function should, in my opinion, definitely be separated from the item itself. Reasons for that are flexibility and modularity. If the valuation function is part of the item, then people would probably soon start to subclass the item class in order to put in their own valuation functions. This might produce all sorts of problems. For example, we would have to make sure that dynamic polymorphism is ensured and working properly everywhere in order for those subclasses to integrate well with the rest of the code. Furthermore, extensive subclassing may produce concurrency issues if people start to make those subclasses stateful. I'm not familiar with how items are currently implemented, but I could imagine some of those issues emerging over time.
Following a more functional approach and passing the valuation function into the item as described by David would provide a great level of flexibility and (presumably) avoid some of the problems mentioned above.
@rafabap I think the key to solving this puzzle is to use closures to inject immutable data into the returned value function so that the value function contains all of the necessary non-item data (i.e., market prices, agent parameters, any other non-item related data) required to value an item. The item could then have a method that takes a valuation function and returns a value or the container object used to store items could have a method that takes a valuation function and then each item.
More thoughts. Valuing an Item
, whether it is a Good
or a Contract
depends on information that is item-specific, information that is agent-specific, and public information.
Examples of Item
instances with item-specific information...
sealed trait Item
trait Good extends Item
trait Contract extends Item
// item specific information is variety, quantity
case class Apple(variety: String, quantity: Long) extends Good
// item specific information is value
case class Currency(value: Long) extends Contract
Agent-specific information would be something like a utility function and its associated parameters, etc. Public information would be something like market prices, etc.
I think it is important to keep the type signature of the valuation function very simple: a generic valuation function takes an instance of a particular type of Item
and maps it to a type representing some kind of Value
trait ValuationFunction[-I <: Item, +V <: AnyVal] extends (I => V)
The approach that I am advocating is that we use closures to inject both agent-specific information and public information (that an agent has access to via various information feeds that it subscribes to) into an instance of a valuation function. This function can then be passed around as need and contains all relevant information needed to value an item.
This modeling decision has many potential ramifications, and deserves careful consideration.
The idea that there is a well-defined mapping from a given item and agent to a scalar valuation in units of price comes from auction-theory, where it is baked into the axioms. Adopting this concept into an API might also have the effect of baking-in various assumptions, which may or may not be warranted.
From a theoretical perspective, auction-theory was originally developed to study single-unit auctions where price-elasticity can be ignored, and each buyer submits a bid that can be considered a signal of its well-defined valuation for the single item being auctioned. However, for multi-unit auctions this concept breaks down, and theoreticians revise the concept of a bid and replace it with a signal analogous to a demand function- i.e. each agent submits an entire function mapping quantities to prices. For practical purposes, however, this is often unfeasible, and so for example in financial order-driven markets traders can submit a discrete approximation of a supply or demand-function by simultaneously submitting limit-orders with different price-quantity combinations. As regards the API implications, you might want to make implementers of your proposed function should aware that the function could be called very many times with different quantity information.
From an empirical perspective, behavioral economics shows us that that if there is such a thing is valuation it is highly context-dependent, and certainly not consistent with expected utility-theory (e.g. real peoples' preferences are intransitive). Therefore I think you are right to make the signature as simple as flexible as possible.
If you are using a dependency-injection framework though, why do you need to use a closure for this? Wouldn't we simply wire-up the valuation component with the dependencies that it requires to do its job? Alternatively, if it is automatically subscribing to the required data feeds, again why do we need to inject this information via a closure?
An alternative view is that closures are a form of light-weight dependency-injection (e.g. https://mikehadlow.blogspot.co.uk/2010/03/functional-dependency-injection.html). However, isn't this decision (to replace DI with functional-programming) orthogonal from the design of a single API call?
On 04/04/17 08:11, David R. Pugh wrote:
More thoughts. Valuing an |Item|, whether it is a |Good| or a |Contract| depends on information that is item-specific, information that is agent-specific, and public information.
Examples of |Item| instances with item-specific information...
sealed trait Item
trait Good extends Item
trait Contract extends Item
// item specific information is variety, quantity case class Apple(variety:String,quantity:Long)extends Good
// item specific information is value case class Currency(value:Long)extends Contract
Agent-specific information would be something like a utility function and its associated parameters, etc. Public information would be something like market prices, etc.
I think it is important to keep the type signature of the valuation function very simple: a generic valuation function takes an instance of a particular type of |Item| and maps it to a type representing some kind of |Value|
trait ValuationFunction[-I <: Item,+V <: AnyVal] extends (I => V)
The approach that I am advocating is that we use closures to inject both agent-specific information and public information (that an agent has access to via various information feeds that it subscribes to) into an instance of a valuation function. This function can then be passed around as need and contains all relevant information needed to value an item.
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/EconomicSL/Core-ESL/issues/14#issuecomment-291414368, or mute the thread https://github.com/notifications/unsubscribe-auth/AEXCge-oBK_9OBpva_AsmjAf-P6WNAHLks5rse0zgaJpZM4MxuFL.
@phelps-sg Thanks for the suggestions. I agree that this design decision deserves careful consideration. Certainly a closure is not the only way to inject the required data in to the function. I am not necessarily averse to using a more heavy weight DI framework to achieve the same effect, but I do not feel that I know enough about the relevant trade-offs between the at least 6 different DI frameworks and approaches to confidently advocate for a particular DI solution at present.
However, isn't this decision (to replace DI with functional-programming) orthogonal from the design of a single API call?
My advocating closures as a solution to this particular problem should not be interpreted as trying to replace DI with functional programming techniques for the entire library.
Alternatively, if it is automatically subscribing to the required data feeds, again why do we need to inject this information via a closure?
The agent would subscribe to the data feeds, but the items that need valuing might live in an object on some other machine in a cluster. If I inject the data into the valuation function using a closure, then I can potentially pass this function as a message to that object on another machine.
On 04/04/17 11:17, David R. Pugh wrote:
@phelps-sg https://github.com/phelps-sg Thanks for the suggestions. I agree that this design decision deserves careful consideration. Certainly a closure is not the only way to inject the required data in to the function. I am not necessarily averse to using a more heavy weight DI framework to achieve the same effect, but I do not feel that I know enough about the relevant trade-offs between the at least 6 different DI frameworks and approaches to confidently advocate for a particular DI solution at present.
However, isn't this decision (to replace DI with functional-programming) orthogonal from the design of a single API call?
My advocating closures as a solution to this particular problem should not be interpreted as trying to replace DI with functional programming techniques for the entire library.
Regardless of your original intention, this would certainly be a valid design philosophy, which need not be rejected outright.
Alternatively, if it is automatically subscribing to the required data feeds, again why do we need to inject this information via a closure?
The agent would subscribe to the data feeds, but the items that need valuing might live in an object on some other machine in a cluster. If I inject the data into the valuation function using a closure, then I can potentially pass this function as a message to that object on another machine.
Distributed OOP via Inter-process communication is not usually compatible with cluster computing, at least in the frameworks I am familiar with. So, for example, if you are using Java RMI, you can set up different services on different machines, e.g. in a client-server relationship, and then have one process invoke methods on another process residing on a different PC. However, typically you need to know the address of the machine you are communicating with. DI frameworks provide some additional loose coupling in this context, by allowing you to inject a proxy stub for the remote service, which you can later configure as being on a remote machine. But you still ultimately need to specify the address when you configure the system. What you are proposing seems to be something much more transparent where you can invoke a method on an object which may be running anywhere on the cluster- is there a particular framework that allows for this?
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/EconomicSL/Core-ESL/issues/14#issuecomment-291457142, or mute the thread https://github.com/notifications/unsubscribe-auth/AEXCgR2_fa5zjfwspAuhoGVWxVF4DJvXks5rshiggaJpZM4MxuFL.
@phelps-sg Next month @bherd-rb and I are going to start experimenting with Akka for this use case. In particular we will be using akka-actors, akka-remoting, and akka-stream.
The Akka module that focuses most specifically on your use case is akka-cluster-sharding. Quoting from the documentation:
In this context sharding means that actors with an identifier, so called entities, can be automatically distributed across multiple nodes in the cluster. Each entity actor runs only at one place, and messages can be sent to the entity without requiring the sender to know the location of the destination actor. This is achieved by sending the messages via a ShardRegion actor provided by this extension, which knows how to route the message with the entity id to the final destination.
@DavoudTaghawiNejad So far we are thinking that valuation of an item should be a member function of every
Item
. It should take as argument the agent that is performing the valuation. This means that theItem
should include all the relevant information necessary to perform its valuation inside the item instance (for example, if an item needs to look at the current market prices, the item needs a reference to the relevant market so that it can obtain the information).In some cases there will be several different valuation functions possible. We are leaning towards putting all of those inside the
Item
and not inside theAgent
that performs them. Because the valuation function takes as argument the identity of the agent that is performing the valuation, the function itself can deal with the different cases and decide which valuation function to use.