-
Notifications
You must be signed in to change notification settings - Fork 122
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Do not require implicit MoneyNumeric in context if calculations are in same currency #300
Comments
So I'm happy to see the Squants team be so active lately. So I thought I would offer a solution to this problem. What we really want in this situation is to give a default value to the implicit variable moneyContext. But the apply methods in the Money object are overloaded and you can't give default values to multiple versions of an overloaded method in Scala. So the only way I found to solve this issue is to create a series of classes that represent each overload of the apply method. Then those classes have implicit conversions to Money and some overrides to make them work with the testing code. The pseudo classes: case class PseudoMoney1(value: BigDecimal) {
def convert(implicit context: MoneyContext = defaultMoneyContext): Money =
new Money(value, context.defaultCurrency)
lazy val del = convert
override def toString: String = del.toString
override def equals(o: Any): Boolean = o match {
case pm: PseudoMoney1 => pm.del.equals(del)
case pm: PseudoMoney2 => pm.del.equals(del)
case pm: PseudoMoneyStr => pm.del.equals(del)
case m: Money => del.equals(m)
case _ => false
}
override def hashCode: Int = del.hashCode
}
case class PseudoMoney2(value: BigDecimal, currency: Currency) {
def convert(implicit context: MoneyContext = defaultMoneyContext): Money =
new Money(value, currency)
lazy val del = convert
override def toString: String = del.toString
override def equals(o: Any): Boolean = {
o match {
case pm: PseudoMoney1 => pm.del.equals(del)
case pm: PseudoMoney2 => pm.del.equals(del)
case pm: PseudoMoneyStr => pm.del.equals(del)
case m: Money => del.equals(m)
case _ => false
}}
override def hashCode: Int = del.hashCode
}
case class PseudoMoneyStr(s: String) {
def convert(
implicit context: MoneyContext = defaultMoneyContext): Try[Money] =
context.dimension(s)
lazy val del = convert.get
override def toString: String = del.toString
override def equals(o: Any): Boolean = o match {
case pm: PseudoMoney1 => pm.del.equals(del)
case pm: PseudoMoney2 => pm.del.equals(del)
case pm: PseudoMoneyStr => pm.del.equals(del)
case m: Money => del.equals(m)
case _ => false
}
override def hashCode: Int = del.hashCode
} Then to the Money class change the equals method to: /**
* Override for Quantity.equal to only match Moneys of like Currency
* @param that Money must be of matching value and unit
* @return
*/
override def equals(that: Any): Boolean = {
that match {
case m: Money ⇒ {
if (currency == m.currency) m.value == value
else if (!m.context.rates.isEmpty) m.in(currency).value == value
else false
}
case pm: PseudoMoney1 ⇒ equals(pm.convert)
case pm: PseudoMoney2 ⇒ equals(pm.convert)
case pm: PseudoMoneyStr ⇒ equals(pm.convert)
case _ ⇒ false
}
} and finally the implicit conversions: implicit def pseudoMoney1ToMoney(pseudo: PseudoMoney1)(
implicit context: MoneyContext = defaultMoneyContext): Money =
pseudo.convert
implicit def pseudoMoney2ToMoney(pseudo: PseudoMoney2)(
implicit context: MoneyContext = defaultMoneyContext): Money =
pseudo.convert
implicit def pseudoMoneyStrToMoney(pseudo: PseudoMoneyStr)(
implicit context: MoneyContext = defaultMoneyContext): Try[Money] =
pseudo.convert And that should solve this issue nicely. Maybe someone else knows of a better way but this seems like the best way to solve this problem here. This way a user always has a currency converter and the user doesn't need to provide one unless they want to override its behavior. It should be noted that the lazy val del (short for delegate) makes this a bit weird. Del exists to prevent unnecessary object creation in some use cases. However, del could use the default money context instead of one declared in the user's scope. To avoid this, just declare the type of the variable to which you assign the return from the apply method. Alternatively, we could just remove del altogether and ignore the issue of unnecessary calls to the implicit conversion. Perhaps the compiler will remove them for us. |
Related to #231
Why do we need a money context if we are in the same currency?
Couple of ways to solve this:
The text was updated successfully, but these errors were encountered: