Dafny如何在C#及其层次结构中实现数据类型?

时间:2018-12-18 14:56:08

标签: c# hierarchy implementation dafny

我实现了一个.Net Web应用程序,它使用在Dafny中完全验证的.dll库。它运作良好,与图书馆的沟通并不困难。这真是棒极了。

不幸的是,代码的某些部分看起来不太好,我想问一下我是不是很好地使用了库,还是使用它的正确方法。我说的是数据类型。为了提出问题,我提供了一个简单的示例。

module DafnyCalculation
{
    datatype Calculation = Sum(s1:int, s2:int) | Rest(r1:int, r2:int) 
                        | Mult(m1:int, m2:int) | Div (d1:int, d2:int)

    function method calculate(cal:Calculation): int
    {
        match cal
            case Sum(s1,s2) => s1+s2
            case Rest(r1,r2) => r1-r2
            case Mult(m1,m2) => m1*m2
            case Div(d1,d2) => 
                if(d2!=0) then d1/d2 
                else d1
    }
}

由于数据类型具有多个构造函数,因此在生成.dll时dafny会自动创建一些类:具有不同参数的Calculation,Base_Calculation,Calculation_Sum,Calculation_Rest,Calculation_Mult和Calculation_Div。我在C#控制台应用程序中以以下方式使用dll:

int result;
Base_Calculation cal;
Console.WriteLine("Enter first number: "); int x = int.Parse(Console.ReadLine());
Console.WriteLine("Enter second number: "); int y = int.Parse(Console.ReadLine());
Console.WriteLine("Choose operator:\n1)Sum\n2)Rest\n3)Mult\n4)Div\nOperator: "); 
int op = int.Parse(Console.ReadLine());
switch (op)
{
    case 1:
        cal = new Calculation_Sum((BigInteger)x, (BigInteger)y);
        break;
    case 2:
        cal = new Calculation_Rest((BigInteger)x, (BigInteger)y);
        break;
    case 3:
        cal = new Calculation_Mult((BigInteger)x, (BigInteger)y);
        break; 
    case 4 :
        cal = new Calculation_Div((BigInteger)x, (BigInteger)y);
        break;
    default:
        throw new Exception("Wrong option");
}
result = (int) _0_DafnyCalculation_Compile.__default.calculate(new Calculation(cal));
Console.WriteLine("Result: {0}", result);
Console.ReadLine();

基于示例,我有一些疑问:

  1. 是否有任何方法可以调用函数calculate(cal:Calculation),而不必构造新的Calculation对象并且直接包含一个“子类型”(Calculation_Sum,Calculation_Rest等)?

  2. 可以_0_DafnyCalculation_Compile .__ default。被避免?

  3. 是否需要导入System.Numerics并使用BigInteger转换C#int和Dafny int?还是可以通过其他方式完成?

非常感谢您。我试图保持说明性和清晰性,如果任何部分无法理解,请随时与我联系。

1 个答案:

答案 0 :(得分:0)

感谢您的提问。

1)是的,现在有。今天,我检查了对编译器的更改,使您可以编写

Calculation.create_Sum(A, B)

代替以前的

new Calculation(new Calculation_Sum(A, B))

如果您的数据类型具有类型参数,则它们将紧随类型名称之后。

2)您可以通过通过_0_DafnyCalculation_Compile提供自己的名称来避免使用{:extern YourNameGoesHere}之类的名称混乱,该名称可以放在模块,类和方法之类的声明上。 (如果某些声明在您需要的地方不支持:extern,请将其作为错误提交。)

(最近我一直在进行一些编译器更改。在很多情况下,我有可能能够自动删除_0_前缀或_Compile后缀。我们将看到。)

3)如果您使用Dafny的类型int,则必须使用所提到的BigInteger。但是,如果要使用本机整数类型,则可以在Dafny中声明自己的整数类型。例如,

newtype int32 = x | -0x8000_0000 <= x < 0x8000_0000

声明其值在给定范围内的Dafny类型int32。 (在此示例中,x的类型为int,但是您也可以显式指定类型,例如newtype int32 = x: int | ...。)Dafny编译器注意到此范围适合带符号的32位整数(在C#中写为int),因此会将您的Dafny类型int32编译为C#的int。这适用于所有本机.NET整数类型。

如果由于某种原因您想要使用比范围所允许的更大的本机整数类型,则可以使用:nativeType属性指示所需的内容。例如,

newtype {:nativeType "short"} byteStoredUsing16Bits = x | -128 <= x < 128

将使用.NET short存储您的byteStoredUsing16Bits类型。如果没有:nativeType属性,Dafny编译器将为sbyte选择.NET类型byteStoredUsing16Bits

您还可以将{:nativeType false}newtype一起使用,以表示您不希望编译器使用本机整数(而是照常使用BigInteger)。

请注意,newtype声明允许您定义自己的整数类型,但是它们与Dafny的标准int不兼容。与声明子集类型(通过使用关键字newtype而非type)来声明子集类型相反,使用newtype的主要原因是允许编译器使用其他表示形式。如果需要在各种整数类型之间进行转换,请使用as运算符。以下代码段说明了这一点:

var x: int := 70;
var y: int32 := x as int32;
var z: byteStoredUsing16Bits := y as byteStoredUsing16Bits;
x := z as int;

从数学上讲,他的as运算符是部分身份函数。也就是说,as永远不会更改值,验证程序将检查您的转换是否来自目标类型中存在的值。 (作为练习,将示例中的70更改为700,并观看验证者的抱怨。)

另外,请注意,newtype类型的所有中间表达式也必须遵守您指定的任何谓词。

Rustan