C#中的依赖类型:使输出类型依赖于输入值

C#中的依赖类型:使输出类型依赖于输入值,c#,type-systems,dependent-type,C#,Type Systems,Dependent Type,我希望能够在C#中创建一个方法,其输出类型取决于其参数值;松散地 代表B(a)DFunc(a) 例如,我想编写一个函数,它接受一个整数并根据参数返回多种可能类型中的一种: f(1) = int f(2) = bool f(3) = string f(n), where n >= 4 = type of n-by-n matrices 任何帮助都是有用的 这并不是一个很好的答案——正如我在评论中提到的,我认为你的要求是不可能的。但这证明了我认为user@douglaszare的建议 p

我希望能够在C#中创建一个方法,其输出类型取决于其参数值;松散地

代表B(a)DFunc(a)

例如,我想编写一个函数,它接受一个整数并根据参数返回多种可能类型中的一种:

f(1) = int
f(2) = bool
f(3) = string
f(n), where n >= 4 = type of n-by-n matrices

任何帮助都是有用的

这并不是一个很好的答案——正如我在评论中提到的,我认为你的要求是不可能的。但这证明了我认为user@douglaszare的建议

  public void RunTest()
  {
     for (int n = 1; n <= 4; n++)
     {
        object o = F(n);

        if (o is int)
           Console.WriteLine("Type = integer, value = " + (int)o);
        else if (o is bool)
           Console.WriteLine("Type = bool, value = " + (bool)o);
        else if (o is string)
           Console.WriteLine("Type = string, value = " + (string)o);
        else if (o is float[,])
        {
           Console.WriteLine("Type = matrix");
           float[,] matrix = (float[,])o;
           // Do something with matrix?
        }
     }

     Console.ReadLine();
  }


  private object F(int n)
  {
     if (n == 1)
        return 42;

     if (n == 2)
        return true;

     if (n == 3)
        return "forty two";

     if (n >= 4)
     {
        float[,] matrix = new float[n, n];
        for (int i = 0; i < n; i++)
           for (int j = 0; j < n; j++)
              matrix[i, j] = 42f;

        return matrix;
     }

     return null;
  }
public void运行测试()
{
对于(int n=1;n=4)
{
浮点[,]矩阵=新浮点[n,n];
对于(int i=0;i
您需要依赖类型来完成此操作。此功能仅存在于少数非主流语言中,如Idris和Coq

既然你已经正确地标记了它,我想你知道c#没有这个功能,那么你具体在问什么/为什么

最接近Agda这样的高级语言的酷特性是参数多态性(泛型)。几乎没有类型推断——而且绝对没有类似于更高级类型、类型类或隐式术语、更高级/非指示性类型、存在量化*、类型族、GADT、任何类型的依赖类型或任何其他您想提及的行话的东西,我不期望会有

首先,人们对它没有兴趣。C是为工业而不是研究而设计的,而绝大多数的C开发者是一个实用的集群,他们中的许多人在“00”中逃离了C++,甚至从未听说过上面列出的大部分概念。设计师们也没有计划添加它们:正如埃里克·利珀特(Eric Lippert)喜欢指出的那样,当你拥有数百万用户时

另一方面,这很复杂。C#以子类型多态性为中心,这是一个简单的想法,它与您可能想要的许多其他类型系统功能有着惊人的深刻交互作用。在我的经验中,少数C#开发者能够理解差异,这只是一个例子。(事实上,具有差异的子类型和泛型的一般情况是。)更多信息,请考虑更高种类的类型(is
Monad m
variant In
m
?),或者类型族在其参数可以被子类型化时应如何行为。大多数高级类型系统都没有使用子类型,这绝非巧合:账户中的货币数量有限,子类型花费了其中很大一部分

也就是说,看看你能把它推多远很有趣。我有一篇题为《泛型的乐趣》(,)的演讲,旨在向毫无戒心的C#用户听众下意识地介绍依赖类型的概念。在详细抱怨类型检查器拒绝的正确程序之后,以及在类型上限方面做了一些愚蠢的事情之后,我将展示如何滥用泛型来模拟依赖类型的最简单示例。以下是这次谈话的荒谬结论:

// type-level natural numbers
class Z {}
class S<N> {}

// Vec defined as in Agda; cases turn into subclasses
abstract class Vec<N, T> {}
class Nil<T> : Vec<Z, T> {}
// simulate type indices by varying
// the parameter of the base type
class Cons<N, T> : Vec<S<N>, T>
{
    public T Head { get; private set; }
    public Vec<N, T> Tail { get; private set; }

    public Cons(T head, Vec<N, T> tail)
    {
        this.Head = head;
        this.Tail = tail;
    }
}

// put First in an extension method
// which only works on vectors longer than 1
static class VecMethods
{
    public static T First<N, T>(this Vec<S<N>, T> vec)
    {
        return ((Cons<N, T>)vec).Head;
    }
}

public class Program
{
    public static void Main()
    {
        var vec1 = new Cons<Z, int>(4, new Nil<int>());
        Console.WriteLine(vec1.First());  // 4
        var vec0 = new Nil<int>();
        Console.WriteLine(vec0.First());  // type error!
    }
}
//类型级自然数
类Z{}
类S{}
//Agda中定义的Vec;案例变成子类
抽象类Vec{}
类Nil:Vec{}
//通过改变
//基类型的参数
类别:Vec
{
公共T头{get;私有集;}
公共向量尾部{get;私有集;}
公共Cons(T头、Vec尾)
{
这个。头=头;
这个。尾巴=尾巴;
}
}
//在扩展方法中放在第一位
//仅对长度大于1的向量有效
静态类向量方法
{
公共静态T优先(此Vec Vec)
{
返回((Cons)vec)头;
}
}
公共课程
{
公共静态void Main()
{
var vec1=新的Cons(4,新的Nil());
Console.WriteLine(vec1.First());//4
var vec0=新的Nil();
Console.WriteLine(vec0.First());//类型错误!
}
}
不幸的是,如果没有先在
内部进行运行时强制转换,则无法完成此操作。
vec
vec
的事实不足以向类型检查器证明它是
Cons
。(你无法证明这一点,因为它不是真的;有人可以在不同的程序集中对
Vec
进行子类化。)更一般地说,没有办法折叠任意的
Vec
,因为编译器无法对自然数进行归纳。这是令人恼火的,因为即使信息在页面上,类型检查器也太笨了,不允许我们获取它

正如Haskell的研究人员所发现的那样,在现有语言上改造依赖类型是很困难的。如果语言是基于子类型(与参数多态性结合起来很复杂)的命令式面向对象语言(通常很难证明定理),则更难。当没有人真正要求时,就更难了


*自从写了这个答案,我对这个话题做了更多的思考,并意识到了这一点。这使您可以使用。

返回对象时,您不喜欢什么?我理解您的问题,直到“玩具示例”,在这一点上,我无法理解您的目标。在你的问题中,你说“输出类型取决于它的参数类型”(我自己强调)。但是,在您的玩具示例中,参数类型始终相同,它始终是
int
。在这里,输出类型似乎取决于参数值。两者都是非常不同的情况,需要不同的答案,所以请澄清您在寻找什么。DouglasZare::请通过可能的实现详细说明@O.R.Mapper::修复了,我只是指参数,而不是它的类型。是的,这取决于参数值。我认为这是不可能的,我也想知道你为什么要这样做。即使有可能,你会如何使用它?您的程序中调用此方法的部分是什么样子的?也许你可以试着解释一下