2010年2月28日日曜日

"MAD MEN" 酒とタバコと女と仕事

四六時中タバコをふかし、仕事中も酒を飲みまくり、女を見ればセクハラまがいの言動を繰り返す。今では信じられない風景ですが、これが50年前、1960年代のアメリカだそうです。

"MAD MEN"とは、広告業界の人間が自分たち自身につけた呼び名。「MADison Squareの男たち」だから"MAD MEN"。「狂った男たち」という意味もあるのかもしれません。

というわけで、アメリカドラマ"MAD MEN"を見始めましたが、感想としては「空気吸う回数よりタバコ吸う回数が多い」「この人たちはタバコ吸わないと死ぬのでは」「演じてる役者が肺がんになりそう」といったところ。とにかくタバコが凄いです。見てるだけで自分の服がタバコ臭くなる感じ。「50年前のアメリカを再現している」からこそ許されるのであって、これがもし今を描いたドラマなら、反タバコ団体に確実に訴えられるでしょう。

オフィスについたらまず酒(ブランデー)、そしてタバコ。ミーティング中も欠かさずタバコ&酒。ランチタイムももちろんタバコ&酒。寝る前のベッドでも忘れずタバコ。火事に気をつけて。

正直、ストーリー(広告代理店に努める主人公の仕事や家族を描いている)はどうでもいいです。最初に見たときは予備知識ゼロだったので、「50年前に作られたドラマなのか?」と思いました。50年前のアメリカに詳しいわけじゃないですが、服装とか髪型とか建物とかよくできてると思います。

Haskellにおける型と"type class"

Scalaとかいう汚い言語を勉強した反動で、美しい言語に触れたくなり、Haskellを触っていた週末でした。

さて、Haskellには "class", "data", "instance", "type" という予約語がありますが、一般的な(オブジェクト指向言語における)意味と全然違うので注意が必要です。

普通に考えると、
  • class: クラス定義?
  • data: インスタンス生成?
  • instance: dataと同じ・・・?
  • type: ???
というように使えそうですが、CやJavaの言葉で説明すると、
  • class: インターフェイス定義用。Javaのinterfaceに近い。
  • data: 構造体定義用。Cのstructに近い。
  • instance: 構造体に属性を定義する。強いて言えばJavaのimplementsに近い。
  • type: 型の同名 (synonim) を作る。Cのtypedef。
となります。

例としてBookという型を考えます。
data Book = Book {
  title :: String,
  authors :: [String],
  isbn :: Int
  }

b = Book "Enjoy Haskell" ["Alice", "Bob"], 12345678
Javaで書くとこう(動くことを確認していません。最近Java触って無いので間違ってるかも)。
class Book {
  public String title;
  public ArrayList authors;
  public int isbn;
}

Book b = new Book;
b.title = "Enjoy Haskell";
b.authors = new ArrayList; // あと省略
b.isbn = 12345678;
ISBNがintで表現できるのかという問題は無視します。)
これでBookクラスを定義できましたが、これだけだといろいろ不便です。例えば(非常に恣意的な例ですが・・・)大小判定ができません。ghciで試してみると・・・
*Main> let b1 = Book "Enjoy Haskell" ["Alice", "Bob"] 1234
*Main> let b2 = Book "Scala Sucks" ["Charlie", "Daniela"] 5678
*Main> b1 < b2

:1:0:
    No instance for (Ord Book)
      arising from a use of `<' at :1:0-6
    Possible fix: add an instance declaration for (Ord Book)
    In the expression: b1 < b2
    In the definition of `it': it = b1 < b2
と、エラーになります。
ご丁寧に"Possible fix: add an instance declaration for (Ord Book)"と、修正方法を教えてくれました。instance declarationを追加してみます。
instance Eq Book where
  (==) a b = (isbn a) == (isbn b)

instance Ord Book where
  compare a b = compare (isbn a) (isbn b)
(今必要なのは、Ord Bookの宣言ですが、OrdはEqのサブクラス(のようなもの)なので、Eq Bookも定義しなくてはいけません。)
これで、大小比較できるようになります。

Javaで同じ事をやるには、Comparableというインターフェイス(で定義されているcomparaメソッド)を実装します。
class Book implements Comparable {
  public int compare(Object o) { return isbn - (Book o).isbn; }
  public String title;
  // 省略
}

というように、Javaでimplementsを使うべきところ、つまりクラスにある性質を追加したいときに、Haskellではinstanceを使います。そして、EqやOrdを"type class"と呼びます。で、type classを定義するのに使う予約語が"class"です。例えばEqとOrdの定義はこうです。
class Eq a where
  (==) :: a -> a -> Bool
  (/=) :: a -> a -> Bool

class (Eq a) => Ord a where
  compare :: a -> a -> Ordering
  (<) :: a -> a -> Bool
  (>=) :: a -> a -> Bool
  (>) :: a -> a -> Bool
  (<=) :: a -> a -> Bool
  max :: a -> a -> a
  min :: a -> a -> a
type classが持つべきメソッドの型を定義しています。Javaだとこうですね。
interface Comparable extends Eq {
  // "extends Eq"はHaskellとの比較のためにつけただけで、実際には不要。

  int compare(Object o);
}

このように、"class", "data", "instance"をJavaと対応付けることで、理解しやすくなると思います。

最後に残った"type"ですが、これはCのtypedefと同じです。
type ErrorNo = Int
Cではこうですね。
typedef int ErrorNo;
別名をつけただけなので、型チェックでは同じ型だとみなされるのもCと同じです。下のコードはtype checkをパスします。
f :: ErrorNo -> String
f e = "Hello World!"

g :: Int -> String
g i = f i

ということで、Haskellにでてくる型関連の予約語を整理してみました。