Programming Language "Cyclone"

http://cyclone.thelanguage.org/

Systems programming in languages other than C? | Lambda the Ultimate
ここを見ていたら、コメント欄にCycloneが良いんじゃない、っていうコメントがあったので調べてみたらあった。


これまたググったらPCASTLみたいな事になるんじゃなかろうかと思ったらそうじゃなかった!
ma2takさんとかshelarcy大先生が補足しているようでした。見ると2001年頃から開発が始まっているようで、その辺は日本語の情報量(断片的ですが)も違うなとか思いました。

ma2takさんによるCycloneの説明
とかのページもあるんですが、私が調べる傍らここに何か色々と書いてみようかと。

取りあえずwikipedia

http://en.wikipedia.org/wiki/Cyclone_programming_language

C言語をより安全(型とかポインタの扱いとか色んな意味で)にした言語で、それでも

without losing the power and convenience of C as a tool for systems programming.

というまぁ今C使うって言うとそうだよなという大事な点を漏らさないようにしている言語らしいです。AT&Tで作られているという事で。


ポインタによるメモリの扱いとかが五月蝿そうだ。


面白そうなfutureで

Garbage collection for heap-allocated values
Tagged unions support type-varying arguments
Polymorphism replaces some uses of void *
Exceptions replace some uses of setjmp and longjmp

これ本当にCやんねー?

wikipediaはもう良いので本家

http://cyclone.thelanguage.org/wiki/User%20Manual
この辺をてけとーに。

と思ったら結構でかいので
http://cyclone.thelanguage.org/wiki/Cyclone%20for%20C%20Programmers#Pointers
ここだけつまみ食い。

Nullable Pointers
int x = 3;
int *y = &x;
*y = *y + 1;

この辺全くもってCと同じ。というかはてなはさっさとcycloneのカラーリングに対応するべき。aliasでも良いから。


これで普通にxを4に出来るけど、このポインタだとy+=1だとかでy自身を変更させる事は出来ない。
それが何を呼び起こすかは良く訓練されたC使いの皆様におきましては容易に想像が付く事と思います。なので省略。

Fat Pointers
    #include <stdio.h>

    int main(int argc, char *@fat *@fat argv) {
      argc--; argv++; /* skip command name */
      if (argc > 0) {
        /* print first arg without a preceding space */
        printf("%s",*argv);
        argc--; argv++;
      }
      while (argc > 0) {
        /* print other args with a preceding space */
        printf(" %s",*argv);
        argc--; argv++;
      }
      printf("\n");
      return 0;
    }

*@fatとすることで、このポインタがfatポインタで有るという事が表せるわけです。が、*@fatとか書くのはめんどいという方の為に

  int main(int argc, char ?? argv);

という手段も提供されています。

Non Null Pointer

その名の通り、Nullにならないと明示的に表すポインタ。
例えば

    FILE *f = fopen("/etc/passwd","r");
    if (f == NULL) {
      fprintf(stderr,"cannot open passwd file!");
      exit(-1);
    }
    int c = getc(f);

これは

    FILE *f = fopen("/etc/passwd","r");
    int c = getc((FILE *@notnull)f); //@notnullがキーワード

と書くのと等しい。@notnullへのキャスト時にnull-checkが入る。

Zero-Terminated Pointers

Fat Pointerはそれはそれで使えるけれども、アクセス安全にする為に余計に確保してしまっている為に、Cのコードとのインタフェースとしてそのまま使えない様なケースもある。


そういう時に使えるのが

A zero-terminated pointer is a pointer to a sequence of elements that are guaranteed to be terminated with a zero.

なpointerであるzero-terminated pointerである。


例えばCの文字列は

char *@zeroterm = str;

とか書ける。

Bounded Pointers

これはポインタの指す要素数がn以上であるようにと指定する様に使う。例えば

void foo(int *@numelts(4) arr);

int x[4] = {1,2,3,4};
int y[8] = {1,2,3,4,5,6,7,8};
foo(x);<-4つでおk
foo(y);<-8つで4以上なのでおk 実際には*@numelts(8)にキャストされる。

int bad[2] = {1,2};
foo(bad); // does not typecheck これが失敗するのは、arr[3]とかでアクセスするとあぼそするからそれを防ぐ為に


どういう所で使えるかというと

  int sum(int num, int *p) {
    int a = 0;
    for (unsigned i = 0; i < num; i++)
      a += p[i];
  }

これは実際pが指す先の想定要素数を上回るインデックスにアクセスする可能性があるので危ない。


これは

int sum(tag_t<`n> num,
        int *@notnull @numelts(valueof(`n)) p) {

こう書くと良い。tag_tに関しては

The type of num is specified as tag_t<`n>. 
This simply means that num holds an integer value, called `n, and the number of elements of p is equal to n. 
This form of dependency is common enough that it can be abbreviated as follows:

  int sum(tag_t num, int p[num]);

and the compiler will fill in the missing information.

とか書かれてる。


ここまでポインタだけで更に色々在るようだけど徹底している様には見える。

他に面白そうなの

Region

例えば、関数の中で一時的に作った値のアドレスを返して、外部でそいつを受け取ってどうやらーという事をさせない為の仕組み。

1 int f() {
2    int x = 0;
3    int *@region(`f) y = &x;
4    L:{ int a = 0;
5        y = &a;
6      }
7    return *y; // yはfのregionなのでその外に出ようとしたらいかん
8 }


こんなの逐一書かせてたら皆がぶちギレルので、コンパイラの方で極力付けてくれる様にしているようである。有り難い。

Tagged Unions

これははぁはぁする

    @tagged union T {
      int Integer;
      const char *@fat String;
    };
    union T x = {.Integer = 3};
    union T y = {.String = "hello, world"};

    bool printT(union T w) {
      if (tagcheck(w.Integer))
        printf("%d",w);
      else
        printf("%s",w);
    }

んな感じでかける。入っていない要素をreadしようとすると例外(<-奥様Cで例外ですよ)が発生する。例えば

    union T a;
    int x;
    a.String = "hello, world";
    /* Next line fails */
    x = a.Integer + 3;

printTはこんな感じでも書ける。

    void printT(union T a) {
      switch (a) {
      case {.Integer = i}: printf("%d",i); return;
      case {.String = s}: printf("%s",s); return;
      }
    }

http://cyclone.thelanguage.org/wiki/Pattern%20Matching
Pattern Matchingについてもっと詳しくは上のページ。

例外
    FILE *f = fopen("/etc/passwd","r");
    int c;
    try {
      c = getc((FILE *@notnull)f); <- fがNULLの時はここで例外が発生
    }
    catch {
    case &Null_Exception:
      printf("Error: can't open /etc/passwd\n");
      exit(1);
    case &Invalid_argument(s):
      printf("Error: Invalid_argument(%s)\n",s);
      exit(1);
    }

こんな事も出来る。

C#でいう所のvarとか
  int foo(int x) {
    let y = x+3;
    let z = 3.14159;
    return (int)(y*z);
  }
subtypingも出来る
  typedef struct Point {float x,y;} *point;

  typedef struct CPoint {float x,y; int color;} *cpoint;

  float xcoord(point p) {
    return p->x;
  }

then you can call xcoord with either a point or cpoint object. 
You can also cast a pointer to a tuple having 3 fields (e.g., $(int,bool,double)) to
a pointer to a tuple having only 2 fields (e.g., $(int,bool)).

しかも

We expect to add more support for subtyping in the future (e.g., subtyping on function pointers, bounded subtyping, etc.)

もっと色々出来るようにしてくれる模様。

Polymorphic function
  $(char*,int) swap1($(int,char*) x) {
     return $(x[1], x[0]);
  }
  $(int,int) swap2($(int,int) x) {
     return $(x[1], x[0]);
  }
  こんな野蛮なコードは書いてられん。

  $(void *,void *) swap1($(void *,void *) x) {
     return $(x[1], x[0]);
  }
  だが我々はこんなコードも書きたく無い。なんだvoid*だって?

  よろしいならば多相型関数だ
  $(`b,`a) swap($(`a,`b) x) {
     return $(x[1],x[0]);
  }

こんな感じ。

もちろん関数だけの話じゃない
  struct List<`a> {`a hd; struct List<`a> * tl; };
  typedef struct List<`a> *list_t<`a>;

  list_t<int> ilist = new List{1,new List{2,null}};
  list_t<string_t> slist = new List{.hd = "foo",
                                  .tl = new List{"bar",null}};

cycloneについて勝手に

C++,Objective-C,Dと比べるとどうかなーっていう。
これCのdialectと書いてるけど素晴らしい意味でCじゃないね。


どのタイミングでこいつを使うか、という事なんだよなぁ。