为什么 scala 没有类似 C++ 的 const 语义?

发布于 2024-09-26 03:17:09 字数 1305 浏览 3 评论 0原文

在C++中。我可以将大多数东西声明为 const,例如:
变量:const int i=5;
Scala 有 val i=5,但这只会阻止重新分配,而不会更改对象,如以下示例所示:
C++:

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)

成员函数的情况会变得更糟:
C++:

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.

当谈到集合时,C++ 延续了 const 集合的 const 正确性:只需将向量声明为 const 并且无法更改它。将非常量向量分配给const向量,编译器不会复制任何内容,并且会阻止您更改当前const集合中的任何内容。

Scala 有 scala.collection.mutable.whatever 和 scala.collection.immutable.whatever,你不能仅仅将可变集合转换为不可变集合,而且你仍然可以使用它们的非常量成员函数来更改收集的对象。

为什么 scala 拥有一个非常棒的类型系统,却没有任何与 C++ const 关键字相媲美的东西?

编辑: Margus 建议使用import scala.collection.mutable。 我的解决方案是使用

import scala.collection.mutable.HashMap
import scala.collection.immutable.{HashMap => ConstHashMap}
This will make the mutable HashMap available as HashMap and the immutable als ConstHashMap, however I still like the C++ approach better.

In C++. I can declare most things as const, for example:
Variables: const int i=5;
Scala has val i=5, however this will only prevent reassigning, not changing the object as the following exampe shows:
C++:

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)

It gets even worse with member functions:
C++:

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.

When it comes to collections, C++ continues it's const-correct streak with const collections: simply declare your vector as const and you can't change it. Assign a non-const vector<Int> to a const vector<Int> and the compiler won't copy anything and will prevent you from changing anything in the now const collection.

Scala has scala.collection.mutable.whatever and scala.collection.immutable.whatever, you can't just convert mutable collections to immutable collections, furthermore you're still allowed to change the collected objects with their non-const member functions.

Why does scala, which has an otherwise really great type-system, not have anything comparable to C++ const-keyword?

Edit:
Margus suggested using import scala.collection.mutable.
My solution was to use

import scala.collection.mutable.HashMap
import scala.collection.immutable.{HashMap => ConstHashMap}

This will make the mutable HashMap available as HashMap and the immutable als ConstHashMap, however I still like the C++ approach better.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

我的痛♀有谁懂 2024-10-03 03:17:09

我不喜欢 C++ const 逻辑的一点是它与引用有关,而不是与被引用的对象有关。如果我有一个“const T *”,则不能保证持有非常量指针的人不会修改该对象的状态。因此,它无助于避免多线程系统中的竞争条件,也无助于实现持久性容器。

在我看来,拥有不可变类的概念非常有帮助,并且标准库中缺乏不可变容器在任何语言中都是一个错误。由于这些应该表现出可观察的不变性,但出于效率原因可能需要能够改变内部/不可见状态,我认为 const 语法没有什么帮助。

Scala 具有我们需要直接使用或基于其他不可变类的不可变类。这是非常有价值的。额外的语法可能是一个很好的补充,但我可以没有它。

The thing I dislike about C++ const logic is that it is about the reference and not about the object that is referenced. If I have a "const T *" there is no guarantee that someone holding a non-const pointer will not modify the state of the object. As such, it does not help in any way to avoid race conditions in multi-threaded systems nor does it help in implementing persistent containers.

In my opinion it is very helpful to have a concept of immutable classes and lack of immutable containers in a standard library is a mistake in any language. Since these should exhibit observable immutability but will likely need to be able to change internal/invisible state for efficiency reasons I think const-syntax would be of little help.

Scala has the immutable classes we need to either use directly or base other immutable classes on. That is extremely valuable. Additional syntax might be a nice addition but I can live without it.

还不是爱你 2024-10-03 03:17:09

因为 C++ const 在复杂系统中并不是那么好。

我可以确定,调用 iPlusFive 不会更改对象,并且我不会意外地对 const 对象调用incrementI。

不,你不能,因为实现(可能在某个看不见的库中)可以消除常量。如果没有 const,其他语言必须以更安全的方式强制执行不变性(例如,请参阅 @Margus 的答案中的 Collections.unmodifyingList() )。

Const 只是编译器读取的文档。文档通常很有帮助,但有时会产生误导。

当涉及到集合时,C++ 继续使用 const 集合来保持 const 正确性:只需将向量声明为 const,并且无法更改它。

对我来说,聚合是 const 经常失败的地方。例如,我经常想声明一个方法不会更改向量,但可能会更改它的成员(或返回非常量成员引用)。我必须将其全部设为常量或全部非常量,或者为每种组合发明新的类型变体。

“可变”弥补了一些聚合问题,但引入了更多的复杂性和误用。

Because C++ const is not that great in complex systems.

I can be sure, that calling iPlusFive won't change the object and that I won't accidentally call incrementI on a const object.

No, you can't, because the implementation (which may be in a library somewhere out of sight) can cast the constness away. Without const, other languages have to enforce the immutability in safer ways (see Collections.unmodifiableList() in @Margus's answer, for instance).

Const is just documentation that the compiler reads. Documentation is usually helpful but sometimes misleading.

When it comes to collections, C++ continues it's const-correct streak with const collections: simply declare your vector as const and you can't change it.

Aggregation is where const often breaks down for me. I often want to declare, for instance, that a method will not change the vector but may change a member of it (or return a non-const member reference). I have to make it all const or all nonconst or invent new type variations for every combination.

'mutable' makes up for some of the aggregation issues but introduces more complexity and misuse.

香橙ぽ 2024-10-03 03:17:09

在我看来,Scala 只是提供了更大的灵活性,不会将引用的不变性与其背后结构的[可能的]可变性混合在一起,使您能够根据正在处理的问题做出设计决策。

IMO, Scala just gives more flexibility not mixing up immutability of the reference with [possible] mutability of the structure behind it, giving you ability to make a design decision WRT the problem you're dealing with.

只想待在家 2024-10-03 03:17:09

所有 Scala 代码都转换为 Java 代码,这就是我用 Java 制作示例的原因。

这是如何在 Java 中执行此操作:

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

那么您问发生了什么? CONST 距离实际数组只有 1 步,它可以隐藏实际数组和 Collection 元素,如果使用简单数组,这是不可能的。如果您仍然想修改常量集合,则需要复制它并进行修改。

来源:链接

具有不可修改内容的数组?

Java没有提供a的概念
const 数组:即一个数组
可以交换为新数组(在 C++ 中
术语,“指针可以修改”),
但其元素不能改变。
但是,如果您需要这个
功能,解决方案是
一般来说很像提供
对任何其他对象的只读访问
如下所述。所以有几个
可能性是:

  • 您可以通过将列表传递给来创建不可修改的列表
    Collections.unmodifyingList() (尽管
    在这种情况下,变量将是
    声明为 List 类型,而不是类型
    将其标记为“不可修改”——如
    下面讨论,任何修改尝试
    它会在运行时被发现);
  • 您可以围绕私有数组创建一个包装对象,并提供
    公共方法可以读但不能写
    它的元素;
  • 您可以使用只读 IntBuffer(或 FloatBuffer 等):...

来源: Java 等效项 - const 从 C++ 的角度来看。

因此 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 与 c++ 编译器不同:

具体化泛型

目前,泛型已实现
使用擦除,这意味着
泛型类型信息不是
在运行时可用,这使得一些
一种很难写的代码。泛型
以这种方式实施以支持
向后兼容旧版本
非通用代码。具体化泛型
将使泛型类型
运行时可用的信息,
这将打破传统的非通用
代码。然而,尼尔·加夫特
提议仅使类型具体化
如果指定,以免破坏
向后兼容性。

资料来源:链接

说起来很愚蠢,但 Java 常量命名约定是使用大写字母表示变量。其他阅读您代码的人会立即知道标识符是一个固定的常量值,无法更改。

来源:链接

...您不能只是将可变集合转换为不可变集合,...

如果您想同时使用集合的可变版本和不可变版本,一个有用的约定是仅导入包 collection.mutable。

import scala.collection.mutable

来源: Scala Collection API

不确定你的意思转换,但你可能可以这样做:

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

All Scala code is translated to Java code, that is why I made examples in Java.

This is how to do this in Java:

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

So what is happening you ask? CONST is 1 step away from actual array, and it enables to hide actual array and the Collection elements, where as using a simple array, this would not be possible. If you would still want to modify constant collection you would need to make a copy of it and modify that.

Source: link

Array with unmodifiable contents?

Java doesn't provide the notion of a
const array: that is, an array that
can be swapped for a new array (in C++
terms, "the pointer can be modified"),
but whose elements can't be changed.
However, if you need this
functionality, the solution is
generally much like providing
read-only access to any other object
as discussed below. So a couple of
possibilities are:

  • you can create an unmodifiable list by passing a list into
    Collections.unmodifiableList() (though
    in this case, the variable would be
    declared of type List, not of a type
    that marked it as "unmodifiable"— as
    discussed below, any attempt to modify
    it would be spotted at runtime);
  • you can create a wrapper object around a private array, and provide
    public methods to read but not write
    its elements;
  • you can use a read-only IntBuffer (or FloatBuffer etc): ...

Source: Java equivalents - const from c++ perspective.

So equivalent for Scala is:

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

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

This should translate to:

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

This might explain my response to IttayD comment, why java 6 differs from c++ compiler:

Reified Generics

Currently, generics are implemented
using erasure, which means that the
generic type information is not
available at runtime, which makes some
kind of code hard to write. Generics
were implemented this way to support
backwards compatibility with older
non-generic code. Reified generics
would make the generic type
information available at runtime,
which would break legacy non-generic
code. However, Neal Gafter has
proposed making types reifiable only
if specified, so as to not break
backward compatibility.

Source: link

Silly to even mention, but Java Constant Naming Conventions is to use uppercase for the variable. Others reading your code will immediately know that the identifier is a fixed, constant value that cannot be changed.

Source: link

... you can't just convert mutable collections to immutable collections, ...

A useful convention if you want to use both mutable and immutable versions of collections is to import just the package collection.mutable.

import scala.collection.mutable

Source: Scala Collection API

Not sure what you mean by converting, but you can probably do:

val a = scala.collection.mutable.List[Int](1, 2, 3)
val A = scala.collection.immutable.List[Int](a.toArray())
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文