是否有一种编程语言,其中类型可以通过值进行参数化?

时间:2010-08-22 13:30:14

标签: programming-languages type-systems

参数化类型(如C ++模板)是一件好事,但大多数时候它们只能通过其他类型进行参数化。

但是,在C ++中有一个特殊情况,可以用整数参数化模板。例如,固定长度数组是一个典型的用例:

template<typename T, int SIZE> class FixedArray 
{ 
    T m_values[SIZE]; 

public: 
    int getElementCount() const { return SIZE; }

    T operator[] (int i) const { 
        if (i<0 || i>=SIZE) 
            throw;
        else 
            return m_values[i]; 
    }  
};

void f()
{
    FixedArray<float, 5> float_array; // Declares a fixed array of 5 floats. 
    //The size is known at compile time, and values are allocated on the stack.
}

在C ++中只允许使用常量整数和指针,但我认为使用任何值进行参数化(浮点数,类实例等)可能会很有趣。这可以允许在编译时表达前提条件(通常在文档中非正式指定),并在运行时自动检查它们。例如,这是假设的C ++方言中的“间隔”模板:

// Interval of type T between TMin and TMax. 
template<typename T, T TMin, T TMax> class Interval
{
    T m_value;

public:
    Interval(int val) { *this = val; }

    Interval& operator = (T val) { 
        //check that 'val is in [TMin, TMax] and set the value if so, throw error if not
        if (val < TMin || val > TMax) 
            throw;
        else
            m_value = val; 

        return *this;
    };  

    operator T() const { return m_value; }
}   

// Now define a f function which is only allowing a float between O and 1
// Usually, that kind of function is taking a float parameter with the doc saying "the float is in 0-1 range". But with our Interval template we have
// that constraint expressed in the type directly.
float f(Interval<float, 0, 1> bounded_value)
{
    // No need to check for boundaries here, since the template asserts at compile-time that the interval is valid
    return ...;
}

// Example usage
void main();
{
    float val = 0.5;

    Interval<float, 0, 1> sample_interval = val;    // OK. The boundary check is done once at runtime.

    f(sample_interval);             // OK. No check, because it is asserted by the type constraint at compile-time.
                                // This can prevent endless precondition testing within an algorithm always using the same interval

    sample_interval = 3*val;            // Exception at runtime because the constraint is no longer satisfied

    f(sample_interval);             // If I am here, I am sure that the interval is valid. But it is not the case in that example.
}   

然后可能有趣的是表达这些类型之间的关系。例如,表达规则以将间隔A分配给具有其他边界的另一个间隔B,或者仅仅是将值分配给间隔的规则,并在编译时检查所有内容。

是否有任何语言采用这种参数化(或类似的方法),还是必须发明一种语言?有用的研究论文?

3 个答案:

答案 0 :(得分:13)

按值参数化的类型称为dependent types。¹对依赖类型的主题进行了大量研究,但很少有人达到“主流语言”。

依赖类型的一个大问题是,如果你的类型包含表达式,即代码位,那么类型检查器必须能够执行代码。这不能完全通用:如果代码有副作用怎么办?如果代码包含无限循环怎么办?例如,请考虑以下类似C语法的程序(省略错误检查):

int a, b;
scanf("%d %d", &a, &b);
int u[a], v[b];

编译器如何知道数组uv是否具有相同的大小?这取决于用户输入的数字!一种解决方案是禁止出现在类型中的表达式中的副作用。但这并不能解决所有问题:

int f(int x) { while (1); }
int u[f(a)], v[f(b)];

编译器将进入无限循环,试图确定uv是否具有相同的大小。

&LT;膨胀&GT;
因此,让我们禁止在类型内部产生副作用,并限制递归和循环以证明终止案例。它是否使类型检查可判定?从理论的角度来看,是的,它可以。你有什么可能像Coq proof term。问题是如果你有足够的类型注释,类型检查很容易判定(类型注释是程序员提供的输入信息)。这里足够意味着很多。太可怕了。在中,在每个单一语言结构中键入注释,不仅是变量声明,还包括函数调用,运算符和其他所有结构。类型将占程序大小的99.9999%。使用所有必需的类型注释编写整个程序,在C ++ 中编写整个程序并调试通常会更快。

因此,这里的困难是使类型系统仅需要合理数量的类型注释。从理论的角度来看,只要您允许省略某些类型注释,它就会成为type inference问题而不是纯类型检查问题。即使是相对简单的类型系统,类型推断也是不可判定的。您不能轻易地使可判断(保证终止)静态(在编译时运行)合理(不需要疯狂数量的类型注释)依赖类型系统。
<子> <强>&LT; /展开&GT;

依赖类型有时在主流语言中以有限的形式出现。例如,C99允许大小不是常量表达式的数组;这种数组的类型是依赖类型。毫无疑问,对于C,编译器不需要检查这样的数组上的边界,即使需要检查常量大小的数组的边界。

更有用的是,Dependent MLML的方言,其类型可以通过简单的整数表达式进行索引。这允许类型检查器静态检查大多数数组边界。

依赖类型的另一个示例出现在ML的模块系统中。模块及其签名(也称为接口)与表达式和类型类似,但它们不是描述计算,而是描述程序的结构。

在大多数程序员认可的意义上,依赖类型经常出现在非编程语言的语言中,而是用于证明程序的数学属性(或仅仅是数学定理)的语言。大多数examples in the wikipedia page具有这种性质。

¹更一般地说,类型理论家根据类型系统是Higher-order types(按类型参数化的类型),polymorphism(按类型参数化的表达式)和dependent types来分类类型系统(由表达式参数化的类型)。此分类称为Barendregt's cube or the lambda cube。实际上它是一个超立方体,但通常第四个维度(由表达式参数化的表达式,即函数)不言而喻。

答案 1 :(得分:5)

我认为你基本上是在描述Dependent Types。从文章中可以看出,有许多(主要是研究)语言实现了这些语言。在一般情况下自动证明类型居住会变得难以处理(即类型检查变得非常困难,或者一般情况下不可判定),但是已经有一些使用它们的实际例子。

答案 2 :(得分:2)

Ada95支持作为值的通用形式参数。在示例on this page中,Size是一个通用形式参数,其值必须是正整数。