DSL example in Scala

Here’s a DSL example in Scala that will allow us to express amounts in the context of currencies taking into account the FX rates too.

By the end of it we’ll be able to write the following:

'USD(100) + 'EUR(200) in 'EUR

Here’s the code for it:

object ScalaForexRatesDSL {

  implicit class ccy2money(ccy :Symbol) {
    def apply(amount: BigDecimal) = Money(amount, ccy)
  }
}

case class FromToCcy(ccyFrom: Symbol, ccyTo: Symbol)

case class Money(amount:BigDecimal, ccy:Symbol) {

  def + (other: Money)(implicit rates: Map[FromToCcy, BigDecimal]): Money =
    if(ccy == other.ccy) Money(amount + other.amount, ccy) else {
      val convertedOther = convertedOtherMoney(other)
      Money(amount + convertedOther.amount, ccy)
    }

  def - (other: Money)(implicit rates: Map[FromToCcy, BigDecimal]): Money =
    if(ccy == other.ccy) Money(amount - other.amount, ccy) else {
      val convertedOther = convertedOtherMoney(other)
      Money(amount - convertedOther.amount, ccy)
    }

  def * (other: Money)(implicit rates: Map[FromToCcy, BigDecimal]): Money =
    if(ccy == other.ccy) Money(amount * other.amount, ccy) else {
      val convertedOther = convertedOtherMoney(other)
      Money(amount * convertedOther.amount, ccy)
    }

  def / (other: Money)(implicit rates: Map[FromToCcy, BigDecimal]): Money =
    if(ccy == other.ccy) Money(amount / other.amount, ccy) else {
      val convertedOther = convertedOtherMoney(other)
      Money(amount / convertedOther.amount, ccy)
    }

  private def convertedOtherMoney(other: Money)(implicit rates: Map[FromToCcy, BigDecimal]): Money = {
    val rate = rates.get(FromToCcy(other.ccy, ccy)).getOrElse(1 / rates.get(FromToCcy(ccy, other.ccy)).get)
    Money(other.amount * rate, ccy)
  }

  def in(ccy: Symbol)(implicit rates: Map[FromToCcy, BigDecimal]): Money =
    if (this.ccy == ccy) this else {
      val rate = rates.get(FromToCcy(this.ccy, ccy)).getOrElse(1 / rates.get(FromToCcy(ccy, this.ccy)).get)
      Money(amount * rate, ccy)
    }

  override def toString = amount + " " + ccy.name
}


object Main extends App {

  import ScalaForexRatesDSL._

  implicit val rates = Map((FromToCcy('EUR, 'USD) -> BigDecimal(1.13)))

  print( 'USD(100) + 'EUR(200) in 'EUR )

}