Подтвердить что ты не робот

Можно ли ограничить Int, создав что-то вроде PositiveInt и проверив время компиляции в Scala?

Можно ли создать ограниченный Int, такой как PositiveInt, и проверить его на время компиляции? Другими словами, можно определить такой метод, как:

def myMethod(x: PositiveInt) = x + 1

а затем введите что-то вроде:

myMethod(-5) // does not compile
myMethod(0)  // does not compile
myMethod(5)  // compiles

Если это возможно, как мне начать с определения PositiveInt, я имею в виду, есть ли удобный способ для этого в Scala?

4b9b3361

Ответ 2

Вы можете использовать признак маркера для типов примитивов следующим образом

trait Positive
type PosInt = Int with Positive
def makePositive(i: Int): Option[PosInt] = if(i < 0) None else Some(i.asInstanceOf[PosInt])
def succ(i: PosInt): PosInt = (i + 1).asInstanceOf[PosInt]

Но вы получите только ошибку времени выполнения для записи makePositive(-5). Вы получите ошибку времени компиляции для записи succ(5).

Возможно, возможно написать плагин компилятора, который "поднимет" положительные целые литералы на отмеченный тип.

Изменить

Я не тестировал, есть ли накладные расходы во время выполнения для маркировки примитивных типов таким образом.

Ответ 3

Другие ответы касаются главным образом вопроса о создании типа "положительного числа", который относится к типу "число" таким образом, чтобы компилятор мог гарантировать правильность. Например, с зависимыми типами вы можете подтвердить правильность реализации функций типа abs. Вы не можете сделать это с помощью scala.

Однако вы можете создать тип, представляющий положительные целые числа, и ничего больше. Например:

abstract class Pos {
  def toInt: Int
}

case object Zero extends Pos {
  def toInt: Int = 0
}

case class Next(x: Pos) extends Pos {
  def toInt: Int = 1 + x.toInt
}

object Pos {
  def apply(x: Int): Pos =
    x match {
      case n if (n < 0) => throw new IllegalArgumentException(s"$x is not a positive integer")
      case 0 => Zero
      case n => Next(Pos(n-1))
    }
}

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

Этот подход, вероятно, не будет практичным для ваших целей. Вам либо нужно реализовать все операции для него, либо передать его в значение Int, что эквивалентно просто проверке времени выполнения, поскольку вы потеряете всю безопасность типов, которую вы выиграли, представляя положительные целые числа таким образом.

Это, по сути, то, что произойдет в вашем примере. Поскольку Pos.apply не является безопасным по типу, вы не можете получить ошибку компиляции в

myMethod(-5)

Ответ 4

Если у вас есть проверка времени, зависящая от времени, вы решили проблему с остановкой, или ваш компилятор не всегда гарантирует завершение компиляции, или грамматик вашего языка программирования должен быть завершен Turing, все из которых являются смешными.

Вот разумная альтернатива для зависимой печати во время выполнения

object ScalaDependentTyping extends App {

  implicit class NaturalNumber(val x: Int) {
    assume(x >= 0)
  }

  implicit def toInt(y: NaturalNumber): Int = y.x

  def addOne(n: NaturalNumber) = n+1

  println(addOne(0))
  println(addOne(1))
  println(addOne(2))

  println(addOne(-1))  //exception
}