没有类型转换的语言

发布于 2024-12-01 00:33:56 字数 1621 浏览 4 评论 0原文

我的问题几乎就是标题所说的:是否有可能有一种不允许显式类型转换的编程语言?

为了澄清我的意思,假设我们正在使用某种类似 C# 的语言,具有父 Base 类和子 Derived 类。显然,这样的代码是安全的:

Base a = new Derived();

因为向上继承层次结构是安全的,但

Dervied b = (Base)a;

不能保证安全,因为向下继承层次结构不安全。

但是,无论安全性如何,此类向下转换在许多语言(例如 Java 或 C#)中都是有效的 - 代码将编译,并且如果类型不正确,则在运行时将失败。因此从技术上讲,代码仍然是安全的,但是通过运行时检查而不是编译时检查(顺便说一句,我不喜欢运行时检查)。

我个人认为完整的编译时类型安全非常重要,至少从理论角度来看,最多从可靠代码的角度来看。编译时类型安全的一个后果是不再需要强制转换(我认为这很好,因为它们无论如何都很丑)。任何类似强制转换的行为都可以通过隐式转换运算符或构造函数来实现。

所以我想知道,目前是否有任何 OO 语言在编译时提供如此严格的类型安全性,以至于强制转换已经过时了?即,他们不允许任何不安全的转换操作?或者有什么原因这不起作用?

感谢您的任何意见。

编辑

如果我可以通过例子来澄清,这就是我如此讨厌沮丧的重要原因。

假设我有以下内容(大致基于 C# 的集合):

public interface IEnumerable<T>
{
     IEnumerator<T> GetEnumerator();
     
     IEnumerable<T> Filter( Func<T, bool> );
}

public class List<T> : IEnumerable<T>
{
    // All of list's implementation here
}

现在假设有人决定编写如下代码:

List<int> list = new List<int>( new int[]{1, 2, 3, 4, 5, 6} );
// Let's filter out the odd numbers
List<int> result = (List<int>)list.Filter( x => x % 2 != 0 );

请注意最后一行的强制转换是如何必要的。但这有效吗?不是一般情况下。当然,List.Filter 的实现将返回另一个 List 是有道理的,但这并不能保证(它可以是 List.Filter 的任何子类型)代码>IEnumerable)。即使它在某个时间点运行,更高的版本也可能会改变这一点,从而暴露出代码是多么脆弱。

几乎所有我认为需要向下转型的情况都会归结为类似这个例子的东西 - 一个方法具有某个类或接口的返回类型,但由于我们知道一些实现细节,所以我们有信心向下转型结果。但这是反 OOP 的,因为 OOP 实际上鼓励从实现细节中进行抽象。那么为什么我们还是要这样做,即使是使用纯 OOP 语言呢?

My question is pretty much what the title says: Is it possible to have a programming language which does not allow explicit type casting?

To clarify what I mean, assume we're working in some C#-like language with a parent Base class and a child Derived class. Clearly, such code would be safe:

Base a = new Derived();

Since going up the inheritance hierarchy is safe, but

Dervied b = (Base)a;

is not guarenteed safe, since going down is not safe.

But, regardless of the safety, such downcasts are valid in many languages (like Java or C#) - the code will compile, and will simply fail at runtime if the types aren't right. So technically, the code is still safe, but via runtime checks and not compile-time checks (btw, I'm not a fan of runtime checks).

I would personally find complete compile-time type safety to be very important, at least from a theoretical perspective, and at most from the perspective of reliable code. A consequence of compile-time type safety is that casts are no longer needed (which I think is great, 'cause they're ugly anyways). Any cast-like behaviour can be implemented by an implicit conversion operator or by a constructor.

So I'm wondering, are currently any OO languages which provide such a rigourous type safety at compile-time that casts are obsolete? I.e., they don't any allow unsafe conversion operations whatsoever? Or is there a reason this wouldn't work?

Thanks for any input.

Edit

If I can clarify by example, here's the big reason I hate downcasts so much.

Let's say I have the following (loosely based on C#'s collections):

public interface IEnumerable<T>
{
     IEnumerator<T> GetEnumerator();
     
     IEnumerable<T> Filter( Func<T, bool> );
}

public class List<T> : IEnumerable<T>
{
    // All of list's implementation here
}

Now suppose someone decides to write code like this:

List<int> list = new List<int>( new int[]{1, 2, 3, 4, 5, 6} );
// Let's filter out the odd numbers
List<int> result = (List<int>)list.Filter( x => x % 2 != 0 );

Notice how the cast is necessary on that last line. But is it valid? Not in general. Sure, it makes sense that the implementation of List<T>.Filter will return another List<T>, but this is not guarenteed (it could be any subtype of IEnumerable<T>). Even if this runs at one point in time, a later version may change this, exposing how brittle the code is.

Pretty much all of the situations I can think that require downcasts would boil down to something like this example - a method has a return type of some class or interface, but since we know some implementation details, we're confident in downcasting the result. But this is anti-OOP, since OOP actually encourages abstracting from implementation details. So why do we do it anyways, even in purely OOP languages?

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

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

发布评论

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

评论(3

忱杏 2024-12-08 00:33:56

通过提高类型系统的能力可以逐渐消除沮丧。

针对您给出的示例提出的一种解决方案是添加将方法的返回类型声明为“与此相同”的功能。这允许子类返回子类而不需要强制转换。因此,你会得到这样的结果:

public interface IEnumerable<T>
{
 IEnumerator<T> GetEnumerator();

 This<T> Filter( Func<T, bool> );
}

public class List<T> : IEnumerable<T>
{
    // All of list's implementation here
}

现在强制转换是不必要的:

List<int> list = new List<int>( new int[]{1, 2, 3, 4, 5, 6} );
// Compiler "knows" that Filter returns the same type as its receiver
List<int> result = list.Filter( x => x % 2 != 0 );

其他向下强制转换的情况也提出了通过改进类型系统的解决方案,但这些改进尚未针对 C#、Java 或 C++ 进行。

Downcasts can be gradually eliminated by improving the power of the type system.

One proposed solution to the example you gave is to add the ability to declare the return type of a method as "the same as this". This allows a subclass to return a subclass without requiring a cast. Thus you get something like this:

public interface IEnumerable<T>
{
 IEnumerator<T> GetEnumerator();

 This<T> Filter( Func<T, bool> );
}

public class List<T> : IEnumerable<T>
{
    // All of list's implementation here
}

Now the cast is unnecessary:

List<int> list = new List<int>( new int[]{1, 2, 3, 4, 5, 6} );
// Compiler "knows" that Filter returns the same type as its receiver
List<int> result = list.Filter( x => x % 2 != 0 );

Other cases of downcasting also have proposed solutions by improving the type system, but these improvements have not yet been made to C#, Java, or C++.

后来的我们 2024-12-08 00:33:56

嗯,当然有可能有根本没有子类型的编程语言,那么自然就不需要向下转换。大多数非面向对象语言都属于这一类。

即使在像 Java 这样基于类的 OO 语言中,大多数向下转型也可以通过让基类拥有一个方法来正式替换

Foo meAsFoo() {
   return null;
}

,然后子类将重写该方法以返回自身。然而,这仍然只是表达运行时测试的另一种方式,还有使用起来更复杂的缺点。并且很难在不失去基于继承的子类型的所有其他优点的情况下禁止该模式。

当然,这只有在您能够修改父类的情况下才有可能。我怀疑您可能会认为这是一个优点,但是考虑到人们可以修改父类并因此使用解决方法的频率,我不确定这对于鼓励“好”来说值得多少设计(对于一些或多或少任意的“好”值)。

可以说,如果该语言提供大小写匹配的构造而不是向下转换的表达式,那么它会更加鼓励安全编程:

Shape x = .... ;
switch( x ) {
  case Rectangle r:
    return 5*r.diagonal();
  case Circle c:
    return c.radius();
  case Point:
    return 0 ;
  default:
    throw new RuntimeException("This can't happen, and I, "+
            "the programmer, take full responsibility");
}

但是,如果没有封闭世界,那么在实践中可能会出现问题假设(现代编程语言似乎不愿意做)许多这样的切换将需要程序员知道永远不会发生的default:情况,这很可能会使程序员变得不敏感到最终的投掷。

Well, it's certainly possible to have programming languages that don't have subtyping at all, and then naturally there's no need for downcasts there. Most non-OO language fall into that class.

Even in a class-based OO language like Java, most downcasts could formally be replaced simply by letting the base class have a method

Foo meAsFoo() {
   return null;
}

which the subclass would then override to return itself. However, that would still just be another way to express a run-time test, with the added downside of being more complicated to use. And it would be hard to forbid the pattern without losing all other advantages of inheritance-based subtyping.

Of course, this is only possible if you're able to modify the parent class. I suspect you might consider that a plus, but given how often one can modify the parent class and so use the workaround, I'm not sure how much that would be worth in terms of encouraging "good" design (for some more or less arbitrary value of "good").

A case could be made that it would encourage safe programming more if the language offered a case-matching construct instead of a downcast expression:

Shape x = .... ;
switch( x ) {
  case Rectangle r:
    return 5*r.diagonal();
  case Circle c:
    return c.radius();
  case Point:
    return 0 ;
  default:
    throw new RuntimeException("This can't happen, and I, "+
            "the programmer, take full responsibility");
}

However, it might then be a problem in practice that without a closed-world assumption (which modern programming languages seem to be reluctant to make) many of those switches would need default: cases that the programmer knows can never happen, which might well desensitivize the programmer to the resultant throws.

我偏爱纯白色 2024-12-08 00:33:56

有许多语言都具有鸭子类型和/或隐式类型转换。我当然会想到 Perl;标量类型的子类型如何在内部进行转换的复杂性经常受到批评,但也受到赞扬,因为当它们确实按照您的预期工作时,它们有助于该语言的 DWIM 感觉。

传统的 Lisp 是另一个很好的例子 - 你拥有的只是原子和列表,以及同时存在的 nil 。否则,两者永远不会相遇……

(不过,您似乎来自一个编程语言必然是面向对象、强类型和编译的宇宙。)

There are many languages with duck typing and/or implicit type conversion. Perl certainly comes to mind; the intricacies of how subtypes of the scalar type are converted internally are a frequent source of criticism, but also receive praise because when they do work like you expect, they contribute to the DWIM feel of the language.

Traditional Lisp is another good example - all you have is atoms and lists, and nil which is both at the same time. Otherwise, the twain never meet ...

(You seem to come from a universe where programming languages are necessarily object-oriented, strongly typed, and compiled, though.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文