По каким причинам и / или преимуществам Scala может сравнивать несовместимые объекты? - PullRequest
5 голосов
/ 07 сентября 2011

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

Может кто-нибудь объяснить, в чем причина такой возможности? Или выгода?

Я знаю, как работает Scala в вопросах сравнения. Я спрашиваю ПОЧЕМУ , что так работает.

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

И еще одна вещь - я не смотрю, как использовать сравнение с помощью Scala для некоторых причудливых эффектов, я ищу более строгую проверку ошибок. IOW: я не хочу сравнивать Orange и Apple по цвету, я хочу запретить такое сравнение по умолчанию, если пользователь явно не скажет, что можно сравнивать такие типы.

Пример

class Test
{
  val s : String = "ala"
}

class Foo
{
  val x : Int = 5
}

object Testbed 
{
  def main(args : Array[String])
  {
    val t = new Test
    val f = new Foo
    if (t==f)
      println("match")
    else
      println("no")
  }
}

Ответы [ 4 ]

10 голосов
/ 07 сентября 2011

Ну, простой ответ заключается в том, что == совместим с java.lang.Object.equals().Поскольку любой класс может переопределить equals(), компилятор Scala не может определить результат проверки равенства между двумя объектами, если он не знает класс времени выполнения объекта во время компиляции (т. Е. Статический тип должен быть конечным классом или new вызов должен быть виден компилятору при компиляции проверки на равенство) И метод equals() не переопределяется ни в этом классе, ни в любом суперклассе.

Итак, в вашем примере компилятор действительно может вывести время выполнениякласса t и f и выдают предупреждение (но не ошибку) для проверки на равенство, но на практике случаи, когда класс времени выполнения может быть выведен, довольно редки.Обратите внимание, что scalac уже выдает предупреждения для некоторых сравнений равенства между примитивными типами и типами объектов.

PS Если вы хотите более безопасную проверку равенства, используйте черту Equal в Scalaz.

4 голосов
/ 07 сентября 2011

Равенство в scala - это равенство значений (не ссылка), и то, что равно тому, что вы можете определить, переопределив equals. В основном == не является оператором в Scala, но действует как (и фактически является) методом на объекте, который вы хотите проверить на равенство. Таким образом, t == f на самом деле t.==(f), где == определено для Any, так что вы получите его на каждом классе в Scala.

Например (в вашем примере) вы можете сделать свой тест == для Foo, например:

class Test {
  val s : String = "ala"
}

class Foo {
  val x : Int = 5
  override def equals(that: Any) : Boolean = {
    that.isInstanceOf[Test] && this.x == 5 && that.asInstanceOf[Test].s=="ala";
  }  
}

и теперь вы получите:

scala> val t = new Test
t: Test = Test@86a58a

scala> val f = new Foo
f: Foo = Foo@104f889

scala> f==t
res3: Boolean = true

но (так как мы НЕ переопределяем равно в тесте)

scala> t==f
res4: Boolean = false

Хотя в этом конкретном случае это не имеет большого смысла, дело в том, что scala позволяет вам решить, что делает Test равным Foo. Вы хотите, чтобы Лицо было == по отношению к другому Лицу (или Сотруднику), если у них одинаковый номер социального страхования? Вы можете реализовать эту логику.

Однако, с большой властью приходит большая ответственность, и на самом деле концепция равенства удивительно хитрая .

1 голос
/ 29 сентября 2011

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

% scalac3 ./a.scala 
./a.scala:20: warning: Test and Foo are unrelated: they will most likely never compare equal
    if (t==f)
         ^
one warning found
0 голосов
/ 01 августа 2016

Я ищу более строгую проверку ошибок.

Недавний (май 2016 г.) пост " Multiversal Equality for Scala ", написанный самим создателем Scala Мартином Одерским , пытается решить эту проблему.

Текущий статус в Scala заключается в том, что компилятор выдаст предупреждения для некоторых сравнений, которые всегда ложны. Но охват слабый. Например, это выдаст предупреждение:

scala> 1 == "abc"
<console>:12: warning: comparing values of types Int and String using `==' will always yield false

Но это не будет:

scala> "abc" == 1
res2: Boolean = false

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

Самый известный способ охарактеризовать такие отношения - это классы типов.
Неявные значения trait Eq[T, U] могут захватывать свойство, что значения типа T могут сравниваться со значениями типа U.
Вот определение Eq:

package scala
trait Eq[-T, -U]

Учитывая набор Eq экземпляров, идея состоит в том, что компилятор Scala будет проверять каждый раз при обнаружении потенциально проблемного сравнения значений типов T и U, что существует неявный экземпляр Eq[T, U].
Сравнение потенциально проблематично, если оно между несовместимыми типами.
Пока T <: U или U <: T равенство может иметь смысл, потому что обе стороны могут потенциально иметь одинаковое значение.

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