lambdista / money   0.8.0

Apache License 2.0 GitHub

Scala DSL for money-related operations

Scala versions: 2.13 2.12

Scala DSL for money-related operations

This Domain-Specific Language (DSL) lets you perform operations among different currencies, by transparently doing all internal conversions. The conversion map is injected implicitly by the client code.

Using Money

As a first step you need to add the resolver and dependency to your build file:

libraryDependencies += "com.lambdista" %% "money" % "0.8.0"

You can find all the released versions here.

Builds are available for Scala 2.13.x, 2.12.x.

Usage Example

Here's a simple usage example:

import money._

object Usage {
  def main(args: Array[String]): Unit = {
    val conversion: Conversion = Map(
      (EUR, USD) -> 1.13,
      (EUR, GBP) -> 0.71,
      (USD, EUR) -> 0.88,
      (USD, GBP) -> 0.63,
      (GBP, EUR) -> 1.40,
      (GBP, USD) -> 1.59
    )

    implicit val converter = Converter(conversion)

    val sumAndConversion1 = 100.001 (USD) + 200 (EUR) to GBP
    println(s"sumAndConversion1: $sumAndConversion1")

    val sumAndConversion2: Money = 100 (USD) + 210.4 (EUR) to EUR
    println(s"sumAndConversion2: $sumAndConversion2")

    val sum              = 100.001 (USD) + 200 (EUR)
    val simpleConversion = sum to GBP
    println(s"simpleConversion: $simpleConversion")

    val sumWithSimpleNumber = 100 (USD) + 23.560
    println(s"sumWithSimpleNumber: $sumWithSimpleNumber")

    val multiplicationWithSimpleNumber = 100 (USD) * 23
    println(s"multiplicationWithSimpleNumber: $multiplicationWithSimpleNumber")

    val usd = Currency("USD")

    val multiplication = 100 (usd) * 23
    println(s"multiplication: $multiplication")

    val divisionWithSimpleNumber = 100 (USD) / 23
    println(s"divisionWithSimpleNumber: $divisionWithSimpleNumber")

    val comparison = 100 (USD) > 99 (EUR)
    println(s"100 USD > 99 EUR? $comparison")
  }
}

As you can see the client code just needs a simple import and an implicit value of type Converter in order to use the DSL. The operations shown in the previous code are only a few among the available ones. Have a look at the Money class for a complete coverage.

Run the example

To run the previous example launch:

$ sbt run

Play with the REPL

To play with Scala's REPL launch:

$ sbt console

This will automatically fire the Scala's REPL and run the following commands for you:

import money._

val conversion: Conversion = Map(
  (EUR, USD) -> 1.13,
  (EUR, GBP) -> 0.71,
  (USD, EUR) -> 0.88,
  (USD, GBP) -> 0.63,
  (GBP, EUR) -> 1.40,
  (GBP, USD) -> 1.59
)

implicit val converter = Converter(conversion)

This way you can start playing with the DSL expressions (e.g.: 100(USD) + 90(EUR)) without worrying about imports and the conversion map. Of course if you need to use your own conversion you can redefine it.

Scala's Numeric type class implementation

When using the following import:

import money._

you get the default implicit for Numeric[Money] whose implementation is as follows:

implicit def numericMoney(implicit converter: Converter) = new NumericMoney(DEFAULT_CURRENCY)

It uses DEFAULT_CURRENCY, which is USD. That's necessary for the fromInt method of Numeric:

def fromInt(x: Int): Money

That is, in order to create a Money object you need a Currency.

If you intend to use a different currency you can define your own implicit Numeric[Money], e.g.:

implicit def numericMoney(implicit converter: Converter) = new NumericMoney(EUR)

Bugs and Feedback

For bugs, questions and discussions please use the Github Issues.

License

Copyright 2014-2021 Alessandro Lacava.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.