ceylonとか話題になってるらしいが、あえてpizzaを紹介してScalaの誕生理由とかAlgebraic Data Typesとかいろいろ考えてみる

なんだか、最近ceylonっていう新しいJVM上の言語がでたらしく、自分のTL上では話題になってました・・・


が、自分は興味わかないというか、Scalaがこれだけtwitterをはじめものすごく実用でつかわれてるのに、今更新しい言語つくって普及させるとかどうなの?という立場です。まぁ言語仕様もちゃんと見てないであれなんですが(・ω・`)


だれかceylonの魅力を延々と語ってください。


たしかに、Scalaは複雑すぎる的な印象は仕方ない部分もあるかもしれないですが・・・とか考えてるときに、そういえばScalaを作成する以前にodersky先生がつくっていた言語あったよなーとか思い出して、前から書こうかと思っていたのでpizzaについて紹介してみます!というよくわからない流れヽ(`▽´)/

pizzaというのは、Scalaの作者であるodersky先生がScalaを作る前につくっていたJVM上で動く言語ですが、現在は開発は完全にStopしているようです。Scalaと違いもともと実用を目指すというよりは、研究目的だったのでしょうかね?

まずpizzaの公式ページ(・ω・)ノ
http://pizzacompiler.sourceforge.net/
大きな特徴として

  • Generics (aka Parametric polymorphism)
  • Function pointers (aka First-class functions)
  • Class cases and pattern matching (aka Algebraic types)

という3つがあげられています。
まぁ論よりコード(?)というかとにかく実際のコード見てみましょう。

class StoreSomething<A> {
  A something;
  
  StoreSomething(A something) {
    this.something = something;
  }
  
  void set(A something) {
    this.something = something;
  }
  
  A get() {
    return something;
  }
}

public class Test {

  public static void main(String[] args) {
     StoreSomething<String> a = new StoreSomething("I'm a string!");
     StoreSomething<int> b = new StoreSomething(17+4);

     b.set(9);

     int i = b.get();
     String s = a.get();
  }
}

class StoreString extends StoreSomething<String> { 
  StoreString(String something) {
    super(something);
  }
}

class StoreSomething2<A> extends StoreSomething<A> {
  
  StoreSomething2(A something) {
    super(something);
  }
  
  void print() {
    System.out.println(""+something);
  }
}

上記のコードは、公式ページにあるサンプルをそのまま持ってきたものです。えー・・・どうみてもJavaですね。しかしこれが、Javaジェネリックが入る前の時代に作られていたというのがポイントです。そもそもodersky先生は、Javaジェネリックコンパイラの開発に参加してたらしいですが、明らかにこのpizzaの言語の研究成果が生かされているという感じですね。*1
上記のコードで中でのJavaとの違いといえば、<Integer>ではなく<int>となっているくらいでしょうか?
あとは、

 interface Set<A implements Comparable> {
         boolean contains(A x);
         Set<A> include(A x);
     }

という指定方法だったりと微妙に違いますが、かなり現在のJavaに似ていますね。


次に、Function pointersですが、


(argtype, ..., argtype) throws exception, ..., exception -> resulttype.

という指定方法だったようです。検査例外は残っている(?)点がScalaとは異なりますね。
また、Scalaでは関数の型を表す場合 => という表記ですが、pizzaの場合は -> という表記だったようです。以下、関数オブジェクトを使った場合のサンプルコード

abstract class Tree<A> {
  public abstract boolean contains((A, A) -> boolean less, A x);
  public abstract Tree<A> insert ((A, A) -> boolean less, A x);
}

class EmptyTree<A> extends Tree<A> {
  public boolean contains((A, A) -> boolean less, A x) {
    return false;
  }
  public Tree<A> insert((A, A) -> boolean less, A x) {
    return new Branch(x, this, this);
  }
}

class Branch<A> extends Tree<A> {
  A elem;
  Tree<A> left;
  Tree<A> right;
  
  public Branch(A elem, Tree<A> left, Tree<A> right) {
    this.elem = elem;
    this.left = left;
    this.right = right;
  }
  
  public boolean contains((A, A) -> boolean less, A x) {
    if (less(x, elem)) 
      return left.contains(less, x);
    else if (less(elem, x))
      return right.contains(less, x);
    else // we assume total ordering, therefore x.equals(elem)
      return true;
  }
  
  public Tree<A> insert((A, A) -> boolean less, A x) {
    if (less(x, elem)) 
      return new Branch(elem, left.insert(less, x), right);
    else if (less(elem, x))
      return new Branch(elem, left, right.insert(less, x));
    else 
      return this;
  }
}

class StringBranch extends Branch<String> {

  StringBranch(String elem, Tree<String> left, Tree<String> right) {
    super(elem, left, right);
  }

  private static boolean stringLess(String x, String y) {
    return x.compareTo(y) > 0;
  }
  
  public boolean contains(String x) {
    return contains(stringLess, x);
  }
  
  public Tree<String> insert(String x) {
    return insert(stringLess, x);
  }
}

そして、最後にAlgebraic Data Types
今まで紹介した2点、ジェネリックとファーストクラスな関数はScalaにもありますが*2Algebraic Data TypesというのはScalaには直接的にはありませんよね。そもそも関数型の素養がない人にとっては、Algebraic Data Typesという言葉(日本語だと一般的には代数的データ型と呼ばれてるっぽい)自体聞いたことがないと思いますが、いわゆる関数型言語では一般的で、普通は言語組み込みでサポートされている構文のようです。自分もあまり詳しくないですが、例えばHaskellOCamlにはあるようですね。

で、話は戻ってpizzaのAlgebraic Data Typesですが、以下のような構文らしいです。

import Tree.*;

class Tree<A> {
  case Empty;
  case Branch(A elem, Tree<A> left, Tree<A> right);
}

public class Test {
  public static void main(String[] args) {
    Tree<int> t = Branch(2,
			 Branch(1, Empty, Empty),
			 Branch(3, Empty, Empty));
  }
}

case というキーワードを使って定義するようです。そして、switch文の中などでパターンマッチもできるみたいです。

こんな感じで大雑把に見てみましたが、現在のScalaと比べると、

  • 型推論
  • Scalaのようなコンストラクタの構文がなく、Javaと同じ
  • case class
  • xmlリテラル
  • for式
  • traitによるMix inの仕組み
  • 記号がメソッド名やクラス名に使える
  • Any型やNothing型
  • typeという型のaliasを定義する構文
  • Unitではなくvoid
  • actorなどのライブラリ

など、上げればきりがないですが、様々な点でScalaのほうが機能が豊富ですね。*3
このように、Javaをシンプルに拡張した感じで、現在のScalaが"複雑すぎる"と思っている人には、むしろJavaに似ていてとっつきやすような印象が持てます。そのあたりがceylonの思想になんだか似ている気がしたので紹介してみました。


そして、ここからまたちょっと話が変わり、ScalaとAlgebraic Data Typesの話。


他の関数型言語にあって、ScalaにAlgebraic Data Typesがない理由ですが、Object-Oriented Pattern Matchingという論文の中で、

1.2 Why Algebraic Data Types are not Object-Oriented

という章がありその中で以下の4点があげられています

  • Lacking extensibility
  • Lacking of representation independence
  • Too many ways to define data
  • No subtyping

ものすごく大雑把にいってしまえば*4Algebraic Data Typesがオブジェクト指向とは様々な点で相性が悪く、それ故にScalaにはAlgebraic Data Typesが入らなかったようです。そしてこの論文の中には、それを補うというか、Algebraic Data Typeの代わりとしてcase classを導入した理由(?)らしきものも書かれているようです。

まぁこんなことを調べたり、知ったとしても、直接的に実際の業務には役に立つわけではありませんが、単に興味があっただけです。しかし、

言語設計者たちが考えること (THEORY/IN/PRACTICE)

言語設計者たちが考えること (THEORY/IN/PRACTICE)

という本がある程度売れるという状況があるように、こういった、言語の作成されてた経緯や歴史に関しても少なからず知りたいと思っている人はいるのではないかなぁーと思い、今回のような記事かいてみました。

*1:ただ自分は専門家でも、詳しく調べてわけでもないので、あまり詳しいことは知りませんよ(・_・)

*2:というかジェネリックは現在のJavaにもありますが

*3:ちゃんと調べたわけではないので間違ってたらごめんさない(>_<)

*4:というか自分も完全に理解してないのでちゃんと説明できないのですが