| 
 | 
 
Go语言的有些方面其实设计的挺好的,能够细粒度的控制内存布局,又有垃圾收集器来避免内存问题。是易用性和性能的很好折中。但是Go的语法很怪异,在"最丑编程语言"的回答里面都被多次提名。而且缺少继承多态、异常处理、泛型(最近可能会有)等关键特性。如果能有一种编程语言,语法和C一致,有指针和GC,能灵活的控制内存布局,能解决内存安全问题,那我一定会用的。可惜没有见到这样的语言,我只能自己来设计了。 
指针安全性 
 
有指针以后,第一个安全问题就是野指针问题,也就是长生命期的引用了短生命期。我的解决方案是将指针生命期分为三类。生命期: 堆内存/全局变量>参数/返回值>局部变量。编译器检查规则是:长生命期可以引用短生命期,短生命期不能引用长生命期。 
参数和本地生命期情况: 
int* foo4(int *b) { 
  int b2; 
  return &b2; //compile error: 本地生命期 < 参数/返回值生命期 
  return b; //ok 
}指针有一个heap关键字来表示是在堆上分配的。heap关键字有三个作用:1.表明指针是堆上分配的,2.在堆上分配内存,3.将栈上对象boxing到堆上。鉴于heap关键字这么常用,那么这个编程语言就叫heap语言。 
struct C { 
  int b; 
} 
 
int* g; 
void foo1(heap int* b) { 
  g = b; 
} 
void foo2(int* b) { 
  g = heap b; 
} 
void foo3(int b) { 
  g = heap b; 
} 
 
x := C(); 
foo1(&x.b) //compile error 
foo1(head x.b) 
foo2(&x.b) 
foo3(x.b) 
 
y := heap C(); 
foo1(&y.b) 
foo2(&y.b) 
foo3(y.b)结构体内部成员的生命期取决于父级的生命期。 
两个未标明参数的相互引用比较棘手,暂时先当错误处理。例如: 
struct A { 
  int *b; 
} 
void foo(A* a, int* b) { 
  a.b = b; //compile error 
} 
void foo(A* a, heap int* b) { 
  a.b = b; //ok 
}返回值生命期提升问题。感谢Nugine提出这个问题,我之前没有想到。 
A foo() { 
  int i; 
  A a; 
  a.b = &i; 
  return a; //compile error 
}这种情况需要对返回值进行严格检查,返回的结构体中不能包含非heap指针。 
继承与多态 
 
我觉得继承和多态是语言抽象能力的重要体现。并且这种抽象没有性能开销。如果信了组合优于继承的话,可以不用这个特性,不是强制的。 
和Java一样的单类多接口继承。只有标记为virtual的结构才能有虚方法。 
interface I { 
  virtual void foo() { ... } 
} 
 
virtual struct B { 
  int a; 
  virtual void bar() { ... } 
} 
 
struct A : B, I { 
  B* b; 
  override void foo(B* b) { 
    ... 
  } 
  override void bar() {...} 
}因为有GC,所以不需要RAII,不需要构造函数和析构函数。 
指针使用 
 
指针使用点语法调用方法,而不用-> 
A a; 
A* b; 
a.foo(); 
b.foo();指针类型转换。 
A *a = dynamic_cast<A*>(p); 
B *a = unsafe_cast<B*>(p);指针默认不允许运算,可临时转为数组处理。 
数组 
 
数组定义和C一致,但是传参数时变成一种自带长度的胖指针。数组在运行时检查越界。 
int[14] a; 
foo(int[] a) { 
  print(a.length); 
  print(a[0]); 
} 
foo(a);数组和指针相互转换 
int *p; 
int[] a = as_array(p, 5); 
 
int *q = a.pointer;异常 
 
众所周知C++中没人使用异常。并不是异常不好,而是异常和手动内存管理不兼容。既然选择了GC,那么异常和GC很搭配。所有可能抛出异常的函数必须使用throws关键字标记。 
void foo() throws { 
} 
 
void dar() throws { 
  foo(); 
} 
 
void bar2() { 
  try { 
    foo(); 
  } 
  catch (Exception *e) { 
  } 
}模板泛型 
 
使用了C++类似的模板来支持泛型。 
struct Bar<T> { 
  void foo(): T { 
    ... 
  } 
}null 
 
指针默认不能为null,除非后面有问号 
B* a; 
B*? a = null;垃圾收集 
 
为什么一定要有GC。手动管理很容易造成内存安全问题。有GC也避免了RAII和析构函数这些机制。GC的开销其实也没有很大,在可接受的范围。 
getter/setter 
 
使用操作符重载的方式来定义getter/setter。 
struct Bar { 
  private int _size; 
  setter void size(int s) { 
    this._size = s; 
  } 
} 
 
Bar b; 
b.size = 2; // call b.size(2);去掉的特性 
 
并不是特性越多越好,去掉很多C++的特性。 
 
- 不支持引用,只有指针。
 
 - 没有class,只有struct。
 
 - 没有头文件
 
 - 不支持函数通过参数重载
 
 - 不支持一次定义多个变量。
 
 - 不支持RAII
 
 - 没有构造函数、析构函数(析构函数功能会有其他的替代方案)
 
 - 不支持嵌套函数、嵌套类、嵌套命名空间等。
 
 - 没有命名空间(有其他的替代机制)
 
 - 不支持宏
 
 - 不支持模板约束,模板元编程。
 
 - 不支持union类型。
 
 - 不需要前向声明
 
 - 没有static的三层意思。
 
 - 别名定义只能在顶部的import语句中。
 
 - 可变性是简单的final,没有C++那么复杂的规则。
 
 - 没有友员机制,默认internal访问级别。
 
 - 不支持多继承,不支持私有继承
 
 - ++i和i++只保留一个,并且不返回任何值。
 
 - 不支持switch语句贯穿
 
 - 不支持do while, goto语句。
 
 
  
其他 
 
后面还会有闭包和运算符重载等,不再详细写,其他语法参考C++。 
基本就是C+OO+GC。没有各种语法糖、没有怪异语法。 
最后,heap语言只是开始设计和征求意见,并没有开始开发。 |   
 
 
 
 |