C#中的依赖类型:使输出类型依赖于输入值
我希望能够在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
代表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#开发者能够理解差异,这只是一个例子。(事实上,具有差异的子类型和泛型的一般情况是。)更多信息,请考虑更高种类的类型(isMonad m
variant Inm
?),或者类型族在其参数可以被子类型化时应如何行为。大多数高级类型系统都没有使用子类型,这绝非巧合:账户中的货币数量有限,子类型花费了其中很大一部分
也就是说,看看你能把它推多远很有趣。我有一篇题为《泛型的乐趣》(,)的演讲,旨在向毫无戒心的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::修复了,我只是指参数,而不是它的类型。是的,这取决于参数值。我认为这是不可能的,我也想知道你为什么要这样做。即使有可能,你会如何使用它?您的程序中调用此方法的部分是什么样子的?也许你可以试着解释一下