Scala: интерполяция чисел

В Scala есть интерполяция строк, например raw"\n" для необработанных строк.

Есть ли в нем что-то вроде интерполяции чисел, например. 1px за один пиксель? Хороший синтаксис для числовых единиц сделает код более читабельным и упростит написание более безопасного кода.

Как и строки, числа имеют приятный литеральный синтаксис и являются фундаментальными.

префиксная нотация px(1) - это не то, как люди пишут единицы:

case class px(n: Int)

И я не думаю, что постфиксная запись через неявное преобразование может работать:

case class Pixels(n: Int) {
 def px() = Pixels(n)
 def +(p: Pixels) = p match {case Pixels(m) => Pixels(n+m)}
}
implicit def Int2Pixels(n: Int) = Pixels(n)
  1. ему нужна точка, пробел или круглые скобки (т. е. не (1 px), (1)px или 1.px, так как люди не пишут единицы измерения).

  2. он не будет проверять типы, т.е. мы хотим явно выполнять приведение между этими числовыми псевдонимами типов и самими числами (т.е. 1.px + 2 и 1 + 2.px и def inc(p: Pixels) = p + Pixels(1) с inc(0) все не терпят неудачу из-за неявного приведения, когда они должны).


person sam boosalis    schedule 15.04.2014    source источник
comment
Я могу ошибаться, но я сомневаюсь, что это может быть достигнуто. Причина в том, что 1f и 1d также считаются числами, и кажется, что Scala не может понять, где заканчивается числовая часть и начинается методическая часть. Ваш подход отлично сработает с Strings: "1"px отлично сработает   -  person serejja    schedule 15.04.2014
comment
Синтаксический анализатор может 1px удалять сахар так же, как он делает s"", с px методом расширения для специального типа вместо Int.   -  person som-snytt    schedule 16.04.2014
comment
@som-snytt можешь объяснить?   -  person sam boosalis    schedule 16.04.2014


Ответы (2)


Вы можете определить собственную интерполяцию строк (подробнее здесь):

implicit class UnitHelper(val sc : StringContext) extends AnyVal {
  def u(args: Any*): Pixels = {
    val pxR = """(\d.*)\s*px""".r   
    sc.parts.mkString match {
      case pxR(px) => Pixels(px.toInt)
      case _ => throw new IllegalArgumentException
    }
  }
} 

Пример использования:

val x = u"10px" + u"20px" // x = Pixels(30)

Плюсы:

  • легко добавить интерполяцию для любых единиц измерения: em, px, pt, cm, in

Минусы:

  • это не безопасно для типов, потому что интерполяция строк является функцией времени выполнения.
person Yuriy    schedule 17.04.2014
comment
спасибо за ответ, но, как я уже сказал, он должен быть типобезопасным. иначе зачем использовать scala, верно? - person sam boosalis; 18.04.2014
comment
вы могли бы сделать его типобезопасным и иметь лучший синтаксис, используя другой StringContext для каждого модуля (т.е. не u10px, а px10). пока вы не конфликтуете со встроенными функциями. - person sam boosalis; 18.04.2014

Попробуйте следующее неявное,

implicit def int2Pixels(n: Int) = new {
  def px = Pixels(n)
}

потом

1 px
res: Pixels = Pixels(1)

Здесь мы вызываем метод px для объекта 1, метод, который не определен в Int, поэтому роль играет неявное.

В целом,

case class Pixels(n: Int) {
  def px() = Pixels(n)
  def +(p: Pixels) = p match {case Pixels(m) => Pixels(n+m)}
  def +(m: Int) = Pixels(n+m)
}

// for enabling implicit conversion
import scala.language.implicitConversions 
implicit def int2Pixels(n: Int) = new {
  def px = Pixels(n)
}

и так

(12 px) + 1
res: Pixels = Pixels(13)

(12 px) + 1 px
res3: Pixels = Pixels(13)

Объяснение импорта см., например, в Почему implicitConversions требуется для неявных определений, но не для классов? .

person elm    schedule 15.04.2014
comment
спасибо за ответ, но я уже предлагал это проблемное решение в своем вопросе, в котором говорится, что я хочу 1. безопасность типов и 2. лучший синтаксис. - person sam boosalis; 16.04.2014