Skip to content

Instantly share code, notes, and snippets.

@pinguinson
Created June 14, 2019 20:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pinguinson/cd12868b86b44759c8af83c6776907a7 to your computer and use it in GitHub Desktop.
Save pinguinson/cd12868b86b44759c8af83c6776907a7 to your computer and use it in GitHub Desktop.
import java.math.MathContext
sealed trait Expression {
def compute: BigDecimal
def show: String
}
sealed trait Amount[T] extends Expression
sealed trait Constant extends Expression
sealed abstract class ConstantAmount[T](currency: String) extends Amount[T] with Constant {
def value: Double
override val compute = BigDecimal.decimal(value, MathContext.DECIMAL64)
override val show = s"${value.toString}$currency"
def +(rhs: Amount[T]) = Add(this, rhs)
def -(rhs: Amount[T]) = Subtract(this, rhs)
def *(rhs: Coefficient) = MultiplyL(this, rhs)
def /(rhs: Coefficient) = Divide(this, rhs)
}
case class Coefficient(value: BigDecimal) extends Constant {
override val compute = value
override val show = value.toString
def *[T](rhs: ConstantAmount[T]) = MultiplyR(this, rhs)
}
sealed abstract class Operation[T](action: (BigDecimal, BigDecimal) => BigDecimal, symbol: String) extends Amount[T] {
def lhs: Expression
def rhs: Expression
override def compute = action(lhs.compute, rhs.compute)
override def show = (lhs, rhs) match {
case (_: Constant, _: Constant) =>
s"${lhs.show} $symbol ${rhs.show}"
case (_: Operation[T], _: Constant) =>
s"(${lhs.show}) $symbol ${rhs.show}"
case (_: Constant, _: Operation[T]) =>
s"${lhs.show} $symbol (${rhs.show})"
case (_: Operation[T], _: Operation[T]) =>
s"(${lhs.show}) $symbol (${rhs.show})"
}
}
case class Add[T](lhs: Amount[T], rhs: Amount[T]) extends Operation[T](_ + _, "+")
case class Subtract[T](lhs: Amount[T], rhs: Amount[T]) extends Operation[T](_ - _, "-")
case class MultiplyL[T](lhs: Amount[T], rhs: Coefficient) extends Operation[T](_ * _, "*")
case class MultiplyR[T](lhs: Coefficient, rhs: Amount[T]) extends Operation[T](_ * _, "*")
case class Divide[T](lhs: Amount[T], rhs: Coefficient) extends Operation[T](_ / _, "/")
case class Dollars(value: Double) extends ConstantAmount[Dollars]("$")
case class Yuan(value: Double) extends ConstantAmount[Yuan]("¥")
case class Pound(value: Double) extends ConstantAmount[Pound]("£")
object MathSyntax {
implicit def doubleToConstant(value: Double): Coefficient = Coefficient(BigDecimal.decimal(value, MathContext.DECIMAL64))
}
object Main extends App {
import MathSyntax._
val operation = Dollars(12.43) + (Dollars(54.62) * 4.55)
println(s"${operation.show} = ${operation.compute}")
// these will not compile
val compilationError1 = Dollars(12.43) / Dollars(43.32)
val compilationError2 = (Dollars(12.43) / 1.01) + (Pound(54.12) * 1.25)
val compilationError3 = Dollars(12.43) + Yuan(43.32)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment