Haskell 静态类型、多态性和专门化
当我第一次学习Haskell时,我很快就爱上了它。这是一个令人愉快的简单想法,效果惊人地好。“如果它编译的话,它通常是正确的”这件事主要是由于参数多态性,IMHO 但有一天,我突然想到了一件事。我可以编写Haskell 静态类型、多态性和专门化,haskell,types,Haskell,Types,当我第一次学习Haskell时,我很快就爱上了它。这是一个令人愉快的简单想法,效果惊人地好。“如果它编译的话,它通常是正确的”这件事主要是由于参数多态性,IMHO 但有一天,我突然想到了一件事。我可以编写foo作为多态函数。但是当bar调用foo时,它将使用一组特定的参数类型来执行此操作。或者,如果bar本身是多态的,那么它的调用者将指定特定的类型。通过归纳,似乎如果您要使用任何有效的Haskell程序并分析整个代码库,您可以静态地确定整个程序中每件事情的类型 从某种意义上讲,这有点像C++模板
foo
作为多态函数。但是当bar
调用foo
时,它将使用一组特定的参数类型来执行此操作。或者,如果bar
本身是多态的,那么它的调用者将指定特定的类型。通过归纳,似乎如果您要使用任何有效的Haskell程序并分析整个代码库,您可以静态地确定整个程序中每件事情的类型
从某种意义上讲,这有点像C++模板。没有运行时多态性,只有编译时多态性。Haskell编译器可以选择为调用每个多态函数的每种类型生成单独的机器代码。大多数Haskell编译器都没有,但如果您愿意,可以实现一个
只有当您开始添加Haskell扩展(existicQuantification
是最明显的扩展)时,您才会开始获得真正的运行时多态性,其中的值的类型不能静态计算
哦,是的,我的问题
import System
foo :: Show a => Int -> a -> IO ()
foo 0 x = print x
foo n x = foo (n-1) [x]
main = do [num_lists] <- getArgs
foo (read num_lists) 0
导入系统
foo::Show a=>Int->a->IO()
foo 0 x=打印x
foo n x=foo(n-1)[x]
main=do[num_list]是的,我也考虑过这个问题。基本上,这个想法是,您似乎可以实现Haskell 98,但不能实现它的一些语言扩展,通过多实例化使用多态性,而不是通过装箱使用多态性
你可以通过尝试将一些Haskell特性实现为C++库(如你所注意到的,C++通过多阶段进行多态),可以对此进行深入了解。你会发现你可以做Haskell能做的一切,除了不可能有多态值,其中包括对多态函数的引用
这看起来像是如果你有
template<typename T>
void f(T); // f :: a -> IO ()
另一方面,存在量化
至少在某些情况下是可行的:它意味着拥有一个带有模板构造函数的非模板类(或者更一般地说,一个类的构造函数模板化的内容比类本身多)
如果在Haskell,您有:
data SomeShow = forall a. Show a => SomeShow a
instance Show SomeShow where show (SomeShow a) = show a
可以在C++中实现为:
// a function which takes a void*, casts it to the given type, and
// calls the appropriate show() function (statically selected based
// on overload resolution rules)
template<typename T>
String showVoid(void *x)
{
show(*(T*)x);
}
class SomeShow
{
private:
void *m_data;
String (*m_show)(void*); // m_show :: Any -> String
public:
template<typename T>
SomeShow(T x)
: m_data(new T(x)) // memory management issues here, but that's orthogonal
, m_show(&showVoid<T>)
{
}
String show()
{
// alternately we could declare the top-level show() as a friend and
// put this there
return m_show(m_data);
}
};
// C++ doesn't have type classes per se, but it has overloading, which means
// that interfaces are implicit: where in Haskell you would write a class and
// instances, in C++ you just write a function with the same name for each type
String show(SomeShow x)
{
return x.show();
}
你能看看那些孔吗!您不能显式地编写它们,但在字典传递实现中,即使在Haskell 98中,具有多态方法的类也会生成包含多态函数的记录。如果你想用多实例来实现整个过程,这显然是个问题。如果您坚持使用Haskell 98,实例几乎总是全局的,静态已知的,因此几乎不需要传递字典就可以成功。每个实例都会产生一些多态函数,但由于在编译时几乎总是知道要调用哪一个,所以几乎不需要在运行时传递对它们的引用(这很好,因为不能)。折衷的办法是,您需要进行整个程序编译,否则实例就不再是静态已知的:它们可能位于不同的模块中。多态递归是个例外,它实际上需要您在运行时构建一个字典。有关这方面的详细信息,请参阅。多态递归即使没有类型类也会扼杀多实例化方法:请参阅关于BTree
s的注释。(另外,带有多态方法的类不再可行,因为您必须再次开始存储指向多态函数的指针。)对。所以它就像我想象的那样是静态的?我觉得有趣的是,这个事实几乎从来都不是问题-是的,我认为GHC实现这一点的方式(使用specialize pragma和all)非常好。多态递归,其他类型的动态调度。即使你将所有多态函数专门化为它们的实际类型,你会发现它们中的许多最终都会得到相同的代码。例如,实际类型被装箱的多态函数的所有专门化最终都是相同的。这将减少代码量。好的,现在你能想出一个多态递归的有用例子吗?;-)[我猜这最终会变得相当复杂…]@MathematicalArchid标准示例涉及到处理完全平衡的树:data BTree a=Leaf a | BTree(a,a)
,仅仅因为不能生成单态版本并不意味着类型在运行时存在:对于大多数程序来说,GHC可以完全消除类型。(尽管类型类字典擦除并不总是可以解决问题——特别是在这样的程序中!)即使多态递归在Haskell中被禁止(就像过去一样),也可以使用类对多态递归进行编码。所以仅仅禁止多态递归不是eno
data SomeShow = forall a. Show a => SomeShow a
instance Show SomeShow where show (SomeShow a) = show a
// a function which takes a void*, casts it to the given type, and
// calls the appropriate show() function (statically selected based
// on overload resolution rules)
template<typename T>
String showVoid(void *x)
{
show(*(T*)x);
}
class SomeShow
{
private:
void *m_data;
String (*m_show)(void*); // m_show :: Any -> String
public:
template<typename T>
SomeShow(T x)
: m_data(new T(x)) // memory management issues here, but that's orthogonal
, m_show(&showVoid<T>)
{
}
String show()
{
// alternately we could declare the top-level show() as a friend and
// put this there
return m_show(m_data);
}
};
// C++ doesn't have type classes per se, but it has overloading, which means
// that interfaces are implicit: where in Haskell you would write a class and
// instances, in C++ you just write a function with the same name for each type
String show(SomeShow x)
{
return x.show();
}
data MonadDict m = MonadDict {
return :: forall a. a -> m a,
(>>=) :: forall a b. m a -> (a -> m b) -> m b
}