Должен ли я использовать неявные преобразования для обеспечения выполнения предварительных условий? - PullRequest
3 голосов
/ 08 января 2011

Мне приходит в голову, что я мог бы использовать неявные преобразования как для объявления, так и для выполнения предварительных условий.Учтите это:

object NonNegativeDouble {
  implicit def int2nnd(d : Double) : NonNegativeDouble = new NonNegativeDouble(d) 
  implicit def nnd2int(d : NonNegativeDouble) : Double = d.v
  def sqrt(n : NonNegativeDouble) : NonNegativeDouble = scala.math.sqrt(n)
}

class NonNegativeDouble(val v : Double ) {
  if (v < 0) {
    throw new IllegalArgumentException("negative value")
  }
}

object Test {
  def t1 = {
    val d : Double = NonNegativeDouble.sqrt(3.0);
    printf("%f\n", d);
    val n : Double = NonNegativeDouble.sqrt(-3.0);
  }
}

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

Во-первых, это:

  1. Хорошая идея,
  2. плохая идея или
  3. очевидная идея, которую все остальные уже знают о

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

Ответы [ 3 ]

9 голосов
/ 08 января 2011

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

Но без зависимых типов и возможности проверки спецификаций во время компиляции, я думаю, это имеет сомнительную ценность, даже если оставить в стороне соображения производительности. Подумайте, как это лучше, чем использовать функцию require для определения начальных условий, требуемых вашим методом, например:

def foo(i:Int) = {
    require (i >= 0)
    i * 9 + 4
}

В обоих случаях отрицательное значение вызовет исключение во время выполнения, либо в функции require, либо при создании вашего NonNegativeDouble. Оба метода четко определяют контракт метода, но я бы сказал, что создание всех этих специализированных типов требует больших затрат, единственная цель которых заключается в инкапсуляции конкретного выражения, которое должно быть утверждено во время выполнения. Например, что, если вы хотите применить немного другое предварительное условие; говорите, что i > 45? Будете ли вы создавать IntGreaterThan45 тип только для этого метода?

Единственный аргумент, который я вижу для построения, например. тип NonNegativeFoo - это если у вас много методов, которые используют и возвращают только положительные числа. Даже тогда, я думаю, что выплата сомнительна.

Кстати, это похоже на вопрос Как далеко зайти со строго типизированным языком? , на который я дал аналогичный ответ.

0 голосов
/ 08 января 2011

Обычно это называется "unsigned int" в C. Я не думаю, что это очень полезно, потому что вы не сможете правильно определить операторы. Учтите это:

val a = UnsignedInt(5)
val b = a - 3 // now, b should be an UnsignedInt(2)
val c = b - 3 // now, c must be an Int, because it's negative!

Следовательно, как бы вы определили оператор минус? Как это может быть:

def -(i:Int):Either[UnsignedInt,Int]

Это сделало бы арифметику с UnsignedInt практически непригодной для использования.

Или вы определяете суперкласс MaybeSignedInt, который имеет два подкласса SignedInt и UnsignedInt. Тогда вы можете определить вычитание в UnsignedInt следующим образом:

def -(i:Int):MaybeSignedInt

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

0 голосов
/ 08 января 2011

На самом деле, довольно изящная идея, хотя я бы не использовал ее ни в каких циклах, чувствительных к производительности.

@ Специализация также может помочь изрядной суммой, чтобы сделать код более эффективным ...

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