哪些类型的系统足够强大来表达这一点?

发布于 2024-12-08 20:56:13 字数 1238 浏览 0 评论 0 原文

假设我们定义了一个这样的接口:

interface Hashable {
   int hash();
}

现在我们可以创建实现这个接口的类。例如 List 类:

class List<T> : Hashable {
   int hash(){
      int h = 0;
      foreach(x in this){
         h = combineHash(h,x.hash());
      }
      return h;
   }
}

问题是我们对 List 内的元素调用 hash,因此 T 必须是 Hashable。所以我们必须这样做:

class List<T : Hashable> : Hashable {
   ... same here ...
}

另一方面,我们也希望能够使用不可散列的元素构造列表。在这种情况下,我们根本不希望 List 实现 Hashable。所以:

List<AHashableType>      is itself Hashable
List<NotAHashableType>   is not Hashable

此外,我们希望哈希方法是多态的(OO 类型的多态,而不是参数多态)。因此,如果我们构造一个包含 BarBazList,其中 FooHashableBarBazFoo 的子类型,具有不同的 hash() 方法,那么List.hash() 应在运行时调用正确的哈希方法。

这似乎是语言应该能够表达的基本事物。相同的模式出现在不同的情况下,例如使用 ToString()IComparable。到目前为止,我还没有找到一种语言和该语言的解决方案,可以让我以类型安全的方式表达这一点。您知道这样的语言和该语言的解决方案吗? Haskell 的类型类非常接近,但不幸的是这些是在编译时调度的。

Suppose that we define an interface like this:

interface Hashable {
   int hash();
}

Now we can make classes that implement this interface. For example a List class:

class List<T> : Hashable {
   int hash(){
      int h = 0;
      foreach(x in this){
         h = combineHash(h,x.hash());
      }
      return h;
   }
}

The problem is that we call hash on elements inside the List<T>, so T has to be Hashable. So we have to do:

class List<T : Hashable> : Hashable {
   ... same here ...
}

On the other hand, we also want to be able to construct lists with elements that are not hashable. In that case we simply don't want List<T> to implement Hashable. So:

List<AHashableType>      is itself Hashable
List<NotAHashableType>   is not Hashable

Furthermore, we want the hash method to be polymorphic (the OO kind of polymorphic, not parametrically polymorphic). So if we construct a List<Foo> with Bar's and Baz's inside, where Foo is Hashable and Bar and Baz are subtypes of Foo with different hash() methods, then that List<Foo>.hash() should call the right hash method at runtime.

This seems like a basic thing that languages should be able to express. The same pattern comes up in different cases, for example with ToString() and with IComparable. So far I haven't found a language and a solution in that language that lets me express this in a type safe way. Do you know of such a language and a solution in that langauge? Haskell comes quite close with its type classes, but unfortunately these are dispatched at compile time.

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

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

发布评论

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

评论(4

中二柚 2024-12-15 20:56:13

不幸的是,您需要一些泛型的条件类型约束(注释模板可以通过部分专业化和私有继承来支持这一点)。我不知道有哪种静态类型语言具有它们,但可以通过扩展方法使用猴子补丁来解决。这是 C# 中的示例。

public static int GetHash<T>(this List<T> list) where T:IEquatable<T>
{
      if(list == null) throw new ArgumentNullException("list");
      int h = 0;
      foreach(var x in list){
         h = CombineHash(h,x.GetHashCode());
      }
      return h;
}

组合哈希 (CombineHash) 组合您的哈希码。
请注意,C# 中的每个方法实际上默认都有一个哈希码,但 IEquatable 是一个将 GetHashCode 指定为成员的接口。
因此 List. 可以调用 GetHash,但 List不能,因为 object 不能。 t 实现接口。
由于 GetHashCode 并不是特别有趣,假设我们这样做了:

interface IFoo{
    string Bar();
}
class Foo:IFoo{
    public virtual string Bar(){return "Foo";}
}
class Baz:Foo{
     public override string Bar(){return "Baz";}
}
public static string Bar(this List<T> list) where T:IFoo
{
    if(list == null) throw new ArgumentNullException("list");
    StringBuilder sb = new StringBuilder();
    foreach(var x in list){
       sb.Append(x.Bar());
    }
    return sb.Bar();
}

这里,由于我们将 T 约束为 IFoo,所以我们现在可以调用它ListListList 上的方法,但不是List as int 未实现 IFoo
例如

public static void SomeMethod()
{
      Console.WriteLine(new List<IFoo>{new Baz(),new Foo()}.Bar()); //compiles
      Console.WriteLine(new List<int>{1,2,3,4,}.Bar()); // does not compile.
}

Unfortunately, you need some how a conditional type constraint for generics (note templates could support this through partial specialization and private inheritance). I don't know a staticly typed language that has them but could be solved with monkeypatching through extension methods.Here is an example in C#.

public static int GetHash<T>(this List<T> list) where T:IEquatable<T>
{
      if(list == null) throw new ArgumentNullException("list");
      int h = 0;
      foreach(var x in list){
         h = CombineHash(h,x.GetHashCode());
      }
      return h;
}

Where CombineHash combines your hashcodes.
Note that every method in C# actually has a hashcode by default, but IEquatable<T> is an interface that specifies GetHashCode as a member.
Thus List<int>. can call GetHash, but List<object> can't as object doesn't implement the interface.
Since GetHashCode isn't particularly interesting let's say we did this:

interface IFoo{
    string Bar();
}
class Foo:IFoo{
    public virtual string Bar(){return "Foo";}
}
class Baz:Foo{
     public override string Bar(){return "Baz";}
}
public static string Bar(this List<T> list) where T:IFoo
{
    if(list == null) throw new ArgumentNullException("list");
    StringBuilder sb = new StringBuilder();
    foreach(var x in list){
       sb.Append(x.Bar());
    }
    return sb.Bar();
}

Here since we constrained T To be an IFoo we are now able to invoke this method on either a List<IFoo>,List<Foo>, or a List<Baz> but not a List<int> as int doesn't implement IFoo.
e.g.

public static void SomeMethod()
{
      Console.WriteLine(new List<IFoo>{new Baz(),new Foo()}.Bar()); //compiles
      Console.WriteLine(new List<int>{1,2,3,4,}.Bar()); // does not compile.
}
注定孤独终老 2024-12-15 20:56:13

也许我误读了OP,但似乎有必要在编译时知道给定的List是否是Hashable。否则,在非 Hashable List 上调用 hash() 会发生什么情况?

不过,我确信 CLOS 存在解决方案。 (多重分派肯定会派上用场,而且我隐约意识到它处理子类的更具表现力的方式。)

Maybe I'm misreading the OP, but it seems like it'd be necessary to know at compile-time whether a given List was Hashable or not. Otherwise, what would happen to a call to hash() on a non-Hashable List?

I'm sure, however, that a solution exists with the CLOS. (Multiple dispatch would certainly come in handy, and I'm dimly aware of its more expressive way of dealing with subclasses.)

拒绝两难 2024-12-15 20:56:13

我认为将您描述的两个问题分开是有用的。第一个是有条件地实现接口。就像你说的,Haskell 类型类就是这样做的。它看起来像:

class Hashable t where
  hash :: t -> Int

instance Hashable Int where
  hash i = i

-- Reads as "(List t) is Hashable if t is Hashable"
instance (Hashable t) => Hashable (List t) where
  hash l = ...

第二个问题是 OO 多态性,您可以在 Haskell 中使用类似函数指针的机制来模拟它。

-- Pretending we have some kind of OO method dispatch here
instance Hashable Foo where
    hash foo = foo.computeHash()

class Foo
   computeHash()

我读过几篇论文,将这种“条件接口实现”添加到类似 Java 的语言中。其中之一是 JavaGI (http://homepages.cwi.nl/~ralf/JavaGI/),它还增加了为最初未定义的数据类型(如 Haskell 类型类)实现接口的能力。我不记得另一篇论文叫什么了。

I think it's useful to separate the two issues you've described. The first is conditionally implementing an interface. Like you said, Haskell type classes do this. It would look something like:

class Hashable t where
  hash :: t -> Int

instance Hashable Int where
  hash i = i

-- Reads as "(List t) is Hashable if t is Hashable"
instance (Hashable t) => Hashable (List t) where
  hash l = ...

The second issue is OO polymorphism, which you can mimic in Haskell with a function-pointer-like mechanism.

-- Pretending we have some kind of OO method dispatch here
instance Hashable Foo where
    hash foo = foo.computeHash()

class Foo
   computeHash()

I've read a couple papers that add this sort of "conditional interface implementation" to a Java-like language. One of them is JavaGI (http://homepages.cwi.nl/~ralf/JavaGI/), which also adds the ability to implement an interface for a data type that you didn't originally define (like Haskell type classes). I can't remember what the other paper was called.

偷得浮生 2024-12-15 20:56:13

恕我直言,您描述的问题是在许多情况下违反接口隔离原则的一个很好的理由,特别是在有限类型的系统(例如 Java 或 .NET 的系统)中。虽然能够使用类型检查作为确保仅要求对象执行它们能够执行的操作的手段具有很大的价值,但并非所有能力问题都可以在程序运行之前决定。在许多情况下,代码将收到对具有可选功能的对象的引用,如果存在,则应使用该对象,如果不存在,则应解决该问题。如果接口包括用于此类可选能力的方法,以及确定是否应使用这些方法的方法,则此类对象的组合或聚合同样可以公开此类可选能力。如果接口仅包含实现者保证能够执行的操作,那么不可变聚合必须在构造时识别它想要宣传的所有功能,而可变聚合不能宣传它们不需要从每个项目中获得的任何功能已添加。

是否存在接口的某些实现具有而其他实现不具有的能力,以及是否很可能相同的对象必须处理具有该能力的事物和不具有该能力的事物,并且应该能够使用当存在这种能力时,我建议在界面中包含该能力以及确定该能力何时可用的方法。例如,如果一个集合标识 IFoo 实例,其中一些实例可以 Boz(),并且集合本身应该实现 IFoo 并调用 Boz 时,集合上的 >Boz 才应该是合法的,我建议让 IFoo 包含 Boz() 以及 CanBoz 属性。 CanBoz 属性可以询问集合中的每个项目是否可以 Boz,只有当所有包含的项目都这样做时才返回肯定。您将失去可能来自更严格的类型检查的编译时保证,但您将能够在静态类型检查不允许的情况下使用该方法。

The problem you describe is IMHO a good justification for going against the Interface Segregation Principle in a number of situations, especially in limited type systems such as those of Java or .NET. While there is great value in being able to use type checking as a means of ensuring that objects are only asked to perform actions of which they are capable, not all questions of ability can be decided before a program is run. There are many situations where code will receive a reference to an object with optional abilities which it should use if present and work around if not. If an interface includes methods for such optional abilities, along with a means of determining whether those methods should be used, then compositions or aggregates of such objects can likewise expose such optional abilities. If interfaces only include actions which implementers are guaranteed to be able to perform, then an immutable aggregation must, on construction, identify all abilities it's ever going to want to advertise, and mutable aggregations cannot advertise any abilities they wouldn't require from every item that's added.

If there is an ability which some implementations of an interface will have and others will not, and if there's a significant likelihood that the same objects will have to deal with things that have the ability and things that don't, and should be able use such ability when present, I would suggest including the ability in the interface along with a means of determining when it is available. For example, if a collection identifies IFoo instances, some of which can Boz(), and the collection itself should implement IFoo and calling Boz on the collection should be legal when and only when all elements can Boz, I'd suggest having IFoo include Boz() along with a CanBoz property. The CanBoz property can ask each item in the collection whether it can Boz, and return affirmative only when all the contained items do so. You would lose the compile-time assurances that could come from stricter type-checking, but you would gain the ability to use the method in those cases where static type checking could not allow it.

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