Scala: уточненный алгебра c типы данных - PullRequest
0 голосов
/ 10 января 2020

o /

Это может быть довольно интересный вопрос, который может вызвать у вас творческий подход.

Я хочу смоделировать валюты таким образом, чтобы я мог:

  • сопоставление с образцом по типу (=> алгебра c тип данных)
  • сохранение числового значения c в нем
  • использование уточненного типа для ограничения значения, например, положительными значениями, например, val amount: Float Refined Positive
  • имеет трехзначный код валюты, такой как "USD", который является предопределенным и неизменным

Выполнение подмножества этого в одной реализации это просто, но я обнаружил, что на удивление трудно создать тип, который допускает что-то вроде следующего:

def doSomething(currency: Currency): Unit {
  currency match {
    case BITCOIN => println("Oh, a cryptocurrency! And it is ${currency.amount} ${currency.code}!"
    case EURO => println("So we are from Europe, eh?")
  }
}

doSomething(new Currency.BITCOIN(123f)) // yielding "Oh, a cryptocurrency! And it is 123 BTC!"

val euro = new Currency.EURO(-42f) // compile error

Надеюсь, я четко изложил свои намерения. Если есть библиотека, которая делает это, я рад, что на нее указывают, хотя я надеюсь кое-что узнать, подумав об этом сам.

1 Ответ

4 голосов
/ 10 января 2020

Вы имеете в виду что-то подобное?

import eu.timepit.refined.api.Refined
import eu.timepit.refined.auto._
import eu.timepit.refined.numeric.NonNegative
import eu.timepit.refined.string.MatchesRegex

sealed trait Currency extends Product with Serializable {
  def amount: Currency.Amount
  def code: Currency.Code
}

object Currency {
  type Amount = BigDecimal Refined NonNegative
  type Code = String Refined MatchesRegex["[A-Z]{3}"]

  final case class Euro(amount: Amount) extends Currency {
    override final val code: Code = "EUR"
  }

  final case class Dollar(amount: Amount) extends Currency {
    override final val code: Code = "USD"
  }
}

def doSomething(currency: Currency): Unit =
  currency match {
    case Currency.Euro(amount) => println(s"Euro: € ${amount}")
    case _ => println(s"Somenthing else with code ${currency.code} and amount ${currency.amount}")
  }

Это работает:

doSomething(Currency.Dollar(BigDecimal(10))) 
// Somenthing else with code USD and amount 10
...