类型类是根据类型解析的,而接口调度是针对显式接收者对象进行的。类型类参数是隐式提供给函数的,而 C# 中的对象是显式提供的。例如,您可以编写以下 Haskell 函数,该函数使用Read
class:
readLine :: Read a => IO a
readLine = fmap read getLine
然后您可以将其用作:
readLine :: IO Int
readLine :: IO Bool
并拥有适当的read
编译器提供的实例。
你可以尝试模仿Read
C# 中带有接口的类,例如
public interface Read<T>
{
T Read(string s);
}
但随后实施ReadLine
需要一个参数Read<T>
你想要的“实例”:
public static T ReadLine<T>(Read<T> r)
{
return r.Read(Console.ReadLine());
}
The Eq
typeclass 要求两个参数具有相同的类型,而你的Eq
接口没有,因为第一个参数隐式是接收者的类型。例如,您可以:
public class String : Eq<int>
{
public bool Equal(int e) { return false; }
}
你不能用它来表示Eq
。接口隐藏了接收者的类型,从而隐藏了参数之一的类型,这可能会导致问题。想象一下你有一个不可变的类型类和接口堆数据结构 https://en.wikipedia.org/wiki/Heap_(data_structure):
class Heap h where
merge :: Ord a => h a -> h a -> h a
public interface Heap<T>
{
Heap<T> Merge(Heap<T> other);
}
合并两个二项堆可以在 O(n) 内完成,而合并两个二项式堆可以在 O(n log n) 内完成,对于斐波那契堆来说,它是 O(1) 。 Heap 接口的实现者不知道另一个堆的真实类型,因此被迫使用次优算法或使用动态类型检查来发现它。相反,实现的类型Heap
类型类确实知道表示形式。