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

Почему scala не имеет С++ - как const-семантика?

В С++. Я могу объявить большинство вещей как const, например:
Переменные: const int i=5;
Scala имеет val i=5, однако это будет только предотвращать переназначение, а не изменять объект, как показывает следующий пример:
С++:

const int i[]={1,2,3,4};
i[2]=5; //error
Scala:
val a=Array(1,2,3,4)
a(2)=5 //a is now Array(1, 2, 5, 4)

Это еще хуже с функциями-членами:
С++:

class Foo {
int i;
int iPlusFive() const {return i+5;}
int incrementI(){ return ++i; }
}
I can be sure, that calling iPlusFive won't change the object and that I won't accidentally call incrementI on a const object.

Когда дело доходит до коллекций, С++ продолжает его const-correct streak с коллекциями const: просто объявляйте свой вектор как const, и вы не можете его изменить. Назначьте non-const vector<Int> a const vector<Int>, и компилятор ничего не скопирует и не позволит вам что-либо изменить в коллекции now const.

Scala имеет scala.collection.mutable.whatever и scala.collection.immutable.whatever, вы не можете просто конвертировать измененные коллекции в неизменяемые коллекции, кроме того, вам по-прежнему разрешено изменять собранные объекты с их неконстантными функциями-членами.

Почему scala, который имеет действительно отличную систему типов, не имеет ничего сопоставимого с ключевым словом С++?

Изменить: Маргус предложил использовать import scala.collection.mutable. Моим решением было использовать

import scala.collection.mutable.HashMap
import scala.collection.immutable.{HashMap => ConstHashMap}
Это сделает изменчивый HashMap доступным как HashMap и неизменяемым als ConstHashMap, однако мне все же нравится подход на С++.
4b9b3361

Ответ 1

То, что мне не нравится в логике С++ const, заключается в том, что речь идет о ссылке, а не об объекте, на который ссылаются. Если у меня есть "const T *", нет гарантии, что кто-то, у которого есть неконтактный указатель, не изменит состояние объекта. Таким образом, это никоим образом не помогает избежать условий гонки в многопоточных системах и не помогает в реализации постоянных контейнеров.

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

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

Ответ 2

Потому что С++ const не так хорош в сложных системах.

Я уверен, что вызов iPlusFive не изменит объект и что я случайно не вызову incrementI в const-объекте.

Нет, вы не можете, потому что реализация (которая может быть в библиотеке где-то вне поля зрения) может вывести константу. Без const другие языки должны обеспечивать неизменность более безопасными способами (см. например Collections.unmodifiableList() в ответе @Margus).

Const - это просто документация, которую компилятор читает. Документация обычно полезна, но иногда вводит в заблуждение.

Когда дело доходит до коллекций, С++ продолжает его const-correct streak с коллекциями const: просто объявляйте свой вектор как const, и вы не можете его изменить.

Агрегация - это то, где const часто ломается для меня. Я часто хочу, например, объявить, что метод не изменит вектор, но может изменить его член (или вернуть ссылку на не константу). Я должен сделать все const или все nonconst или изобретать новые варианты типов для каждой комбинации.

'mutable' компенсирует некоторые из проблем с агрегацией, но вводит большую сложность и неправильное использование.

Ответ 3

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

Ответ 4

Весь код Scala переведен на Java-код, поэтому я сделал примеры на Java.

Вот как это сделать в Java:

    Integer x[] = new Integer[]{1,2,3,4}; 
    final List<Integer> CONST = Collections.unmodifiableList(Arrays.asList(x));

Итак, что вы спрашиваете? CONST находится в 1 шаге от фактического массива и позволяет скрыть фактический массив и элементы коллекции , где, используя простой массив, это будет невозможно. Если вы все равно хотите изменить постоянную коллекцию, вам нужно будет сделать ее копию и изменить ее.

Источник: ссылка

Массив с неизменяемым содержимым?

Java не предоставляет понятия const array: то есть массив, который могут быть заменены на новый массив (в С++ термины, "указатель может быть изменен" ), но чьи элементы не могут быть изменены. Однако, если вам это нужно функциональности, решение как правило, доступ только для чтения к любому другому объекту как обсуждается ниже. Итак, пара возможности:

  • вы можете создать немодифицируемый список, передав список в Collections.unmodifiableList() (хотя в этом случае переменная была бы объявлено типа List, а не типа что обозначало его как "неизменяемое" - как обсуждается ниже, любая попытка изменить он будет замечен во время выполнения);
  • вы можете создать объект-оболочку вокруг частного массива и предоставить публичные методы читать, но не писать его элементы;
  • вы можете использовать IntBuffer для чтения (или FloatBuffer и т.д.):...

Источник: Java-эквиваленты - const с точки зрения С++.

Таким образом, эквивалент для Scala:

    val A = Set(1, 2, 3, 4)

    val A = List(1, 2, 3, 4)

Это должно перевести на:

    scala.collection.immutable.List[java.lang.Integer] A = List(1, 2, 3, 4)

Это может объяснить мой ответ на комментарий IttayD, почему java 6 отличается от компилятора С++:

Обоснованные дженерики

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

Источник: ссылка

Глупо даже упоминать, но соглашения о назначении Java Constant должны использоваться в верхнем регистре для переменной. Другие, читающие ваш код, сразу узнают, что идентификатор является фиксированным постоянным значением, которое не может быть изменено.

Источник: ссылка

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

Полезное соглашение, если вы хотите использовать как изменяемые, так и неизменные версии коллекций, - это импортировать только пакет collection.mutable.

import scala.collection.mutable

Источник: Scala API-интерфейс коллекции

Не уверен, что вы подразумеваете, конвертируя, но вы, вероятно, можете сделать:

val a = scala.collection.mutable.List[Int](1, 2, 3)
val A = scala.collection.immutable.List[Int](a.toArray())