Skip to content

Instantly share code, notes, and snippets.

@j5ik2o
Last active February 11, 2016 01:57
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 j5ik2o/e99968bcdbe5d3555ad7 to your computer and use it in GitHub Desktop.
Save j5ik2o/e99968bcdbe5d3555ad7 to your computer and use it in GitHub Desktop.

インタープリタをトランポリン化できていないが、だいたいやりたいことができた。

Free[Command, Unit]ですが、Unit以外を選択する場合ってどんなユースケースなんだろうか?

case class Money(amount: Long, currency: Currency) {

  def +(other: Money): Money = {
    require(currency == other.currency)
    copy(amount = this.amount + other.amount)
  }

  def -(other: Money): Money = {
    require(currency == other.currency)
    copy(amount = this.amount - other.amount)
  }


}

object Money {

  val JPY = Currency.getInstance("JPY")

}

object AccountType extends Enumeration {

  val Current, Ordinary = Value

}

case class Account(accountNumber: String,
                   accountType: AccountType.Value,
                   balance: Money = Money(0, Money.JPY)) {

  private[test] def deposit(addend: Money) = {
    copy(balance = this.balance + addend)
  }

  private[test] def withdraw(subtrahend: Money) = {
    copy(balance = this.balance - subtrahend)
  }

  override def equals(obj: scala.Any): Boolean = obj match {
    case that: Account => accountNumber == that.accountNumber
    case _ => false
  }

  override def hashCode(): Int = accountNumber.## * 31

}

object AccountDSL {

  sealed trait Command[+A]

  final case class Deposit[A](addend: Money, next: A) extends Command[A]

  final case class Withdraw[A](subtrahend: Money, next: A) extends Command[A]

  case object Done extends Command[Nothing]

  def deposit(addend: Money): Free[Command, Unit] =
    Free.liftF(Deposit(addend, Done))

  def withdraw(subtrahend: Money): Free[Command, Unit] =
    Free.liftF(Withdraw(subtrahend, Done))

  def done: Free[Command, Unit] =
    Free.point(Done)

  implicit val functor = new Functor[Command] {
    override def map[A, B](fa: Command[A])(f: (A) => B): Command[B] = fa match {
      case Deposit(addend, next) => Deposit(addend, f(next))
      case Withdraw(subtrahend, next) => Withdraw(subtrahend, f(next))
      case Done => Done
    }
  }
}

object AccountDSLRunner {

 // TODO: トランポリン再帰化
  private def interpret(acc: Reader[Account, Account], p: Free[Command, Unit]): Reader[Account, Account] = {
    p.resume.fold({
      case Deposit(v, next) =>
        interpret(acc.map(_.deposit(v)), next)
      case Withdraw(v, next) =>
        interpret(acc.map(_.withdraw(v)), next)
    }, { _ => acc })
  }

  def run(program: Free[Command, Unit])(account: Account) = interpret(Reader(identity), program).run(account)

}

object AccountDSLMain extends App {

  lazy val program = for {
    _ <- AccountDSL.deposit(Money(10, Money.JPY))
    _ <- AccountDSL.withdraw(Money(5, Money.JPY))
    _ <- AccountDSL.deposit(Money(10, Money.JPY))
    _ <- AccountDSL.withdraw(Money(5, Money.JPY))
  } yield () // () って、、、どうにかならないのか…。

  val result = AccountDSLRunner.run(program)(Account("12345", AccountType.Ordinary))

  println(result)

}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment