型宣言の必要性2006年11月30日 16時33分28秒

矢印演算子 -> は、何の為に残しておきたかったのかしら?

高木センセーがブクマで以下のように批評していらっさる訳ですが、

例えばJavaで、「LinkedList x = new LinkedList()」と書かずにわざわざ「List x = new LinkedList()」と書くことの意義を理解していない。

それは恐らく instance (Perl 的に言えば invocant) の型ではなくて、それを受け容れる変数そのものが、受け容れる値の型を限定することの意義、ということなのでしょう。確かに、

home = getenv('HOME'); /* home is char * because getenv() returns char * */

getenv()char * を返すから、homechar * だ」という解釈がまかり通ってしまうと、将来的にはこの home という変数には、char * 以外の型の値が入ってきてもいいように見えます (実際 Perl はそんな感じの世界ですね)。Perl は値の型よりもコンテキストが優先される言語であるため、大抵の局面においてはそれで問題がないわけですが、一方でオブジェクトを扱う局面においては、bless された invocant に対して refisa メソッドで所属する package (一般にいうところの class) を調べたり、can メソッドで特定のメソッドが存在するかどうかを調べたりすることによって型の厳密性を担保する必要も出てきます。

そういう意味では、Perl の世界観ってのは、ある程度の物事の厳密性に対して、「そもそもプログラマーが間違えなきゃいいんじゃん」という価値観がまかり通っている部分もあるわけですが、その一方で、間違ったハンガリアン記法の使われ方がその手の間違いを誘発しまくっていたりする現実があったりして、そして世の中の Java やら C++ やら D やらのコンパイラが頭よくなってきてくれたおかげでそもそも型を意識する作業はコンパイラに任せておけばいいという考え方がやっと定着してきているんかなぁというこのご時世において、再びプログラマが型を意識しなきゃならんような言語を、それもスクリプト言語としてではなくてアセンブラのマクロとして提供することの意義ってそんなにあるのかなぁっていう疑問はあるわけですが。。。

しかし次回のテーマが代入演算子としてよく使われている = であるということで、もしかしたらこの辺のことについても実は十分認識していて、その解決策を次回は述べられるのかなぁなどという期待もあったり。。。つまり、C++ では通常の代入は値渡し (すなわち、コピー) で、ポインタの代入が参照渡しになるわけですが、Perl では (摩訶不思議なコピーコンストラクタのカラクリは存在するものの) = は参照渡しとしてしか使えないので、この辺をどうにか切り分けられればいろいろと解決できる問題もあって、そういう世界観ならそもそも型宣言なんて不要なんじゃねーの、という論展開になるんじゃないかと予想するわけですが。。。違うかなぁ? (((((((((/;^^)/


Fri Dec 1 12:47:32 JST 2006 - 追記

tb どもです。

型推論でhomeの型を決定しているのだから、char *以外の型の値が入るわけ無いと思います。

なるほど。だとすると、ますます高木氏のおっさられる通りですやんね。

class Animal {
  // ...
};

class Dog : extended Animal {
  // ...
};

class Cat : extended Animal {
  // ...
};

class DogsCompanions {
  members as list<Dog>;
  iterator_counter as int;
public:
  push(dog as Dog) as void { members.push_back(dog); }
  shift() as Dog { return members.pop_front(dog); }
};

class CatsCompanions {
  // ... (似たような内容)
};

enum ANIMAL_KIND {
  ANIMAL_DOG,
  ANIMAL_CAT,
};

DogsCompanions dogs;
CatsCompanions cats;

getFavoriteAnimal(kind as ANIMAL_KIND) as Animal{
  switch (kind){
    case ANIMAL_DOG:
      animal = dogs.shift;
      break;
    case ANIMAL_CAT:
      animal = cats.shift;
      break;
    default:
      assert(0);
      animal = NULL;
      break;
  }
  
  return animal;
}

さて、getFavoriteAnimal() 関数内の変数 animal は、何の型としてコンパイルされるべきでしょう?

# そもそも、その設計は無いだろうとか言われちゃうと、ちょっとアレなんですが。。。

上記のコードで、変数 animalAnimal 型の変数として認識するようコンパイラに要求するのはさすがに無理があるように思います (return 文を根拠に類推すれば不可能ではないですが)。だとすると、コンパイラは以下のいずれかの理由でエラーを吐くしかありません。

  • 型の曖昧さを解消するよう要求するエラー : 例えば、if 構文や switch 構文の中で使用する変数は、事前に型宣言が必要、とか。
  • 最初の出現時点で Dog 型の変数であると認識し、Cat オブジェクトを代入しようとする箇所で型不一致エラー。

どちらにせよ、変数 animal は事前に型宣言が必要になってしまいます。

# ついでに言うと、NULL は本当は NULL と書くべきではない気もするけど。

コメント

コメントをどうぞ

※メールアドレスとURLの入力は必須ではありません。 入力されたメールアドレスは記事に反映されず、ブログの管理者のみが参照できます。

※投稿には管理者が設定した質問に答える必要があります。

名前:
メールアドレス:
URL:
次の質問に答えてください:
おいらがやっている会社の名前をひらがな4文字で。

コメント:

トラックバック

※トラックバックの受付件数を超えているため、この記事にトラックバックを投稿することができません。

_ 地獄の猫日記 - 2006/12/01 01:30:34

http://harapeko.asablo.jp/blog/2006/11/30/976641 「getenv() は char * を返すから、home も char * だ」という解釈がまかり通ってしまうと、将来的にはこの home という変数には、char * 以外の型の値が入ってきてもいいように見えます (実際 Perl はそんな感じの世界で

_ 地獄の猫日記 - 2006/12/01 21:04:27

class Animal { // ... }; class Dog : extended Animal { // ... }; class Cat : extended Animal { // ... }; class DogsCompanions { members as list&#60;Dog&#62;; iterator_counter as int; public: push(dog as Dog) as void { members.push_back(dog); } shift() as