たけをです。

On 9/28/05, Asaki NISHIKAWA <asakiasakiasaki / yahoo.co.jp> wrote:
> 結局のところ、
> ・手続き型+OOPのスタイルに近づけて組むことは可能だが、それだと意味がないよ
> うな
> ・では、Haskellの利点を生かした組み方って?

 あんまり答えになってない気がしますし、私もHaskellプログラマーレベル1〜
2くらいなので、他の人から「それはちげーよ」ってツッコミが入りそうですが
(^^; 私も本業は手続き型OOPゴリゴリなので…
 Java(もしくは手続き型OOP)な思考からHaskellに移行する場合、第一段階と
して

・カプセル化はしない。オブジェクトとメソッド(関数)は分離して考える。
   当然アクセス制御子などなく、全てpublic (^^;
・関数が主役。「オブジェクトにメソッドが従属する」のではなく、「オブジェ
クトに関数を適用する」と考える。(ちょっと duck typing っぽい?)
・手続き型OOPにおける「継承」や「オーバーローディング」はない。代わりに
型クラスを利用する。

という感じかな、と勝手に思っていたりするのですが。(めちゃめちゃ感覚的な
コメントですいません)


例えばJavaで↓こんなクラスを考えて、
------------------------------
// 2次元座標上の点
class Point {
  private int x, y;
  public Point (int initX, int initY) {
    x = initX; y = initY;
  }
  // 指定された差分だけ移動
  public void move (int dx, int dy) {
    x += dx; y += dy;
  }
  // 自分自身を表示
  public void show () {
    System.out.println("Pt{x="+x+", y="+y+"}");
  }
}
------------------------------

同じことをHaskellで書くと、こんな感じ↓かなぁと。
------------------------------
-- 図形
class Draw a where
  move :: Int -> Int -> a -> a  -- x,y座標を指定された分だけ移動

-- 点
data Point = Pt { x, y :: Int }

instance Draw Point where
  move dx dy Pt {x=a, y=b} = Pt (a+dx) (b+dy)

instance Show Point where
  show Pt {x=a, y=b} = "Pt{x="++(show a)++", y="++(show b)++"}"
------------------------------

どっちかというと、Javaでインタフェースを利用した感覚に近くなるのではない
かと思います。だからJavaの方は、DrawとShowインタフェースがあるとした上で

  class Point implements Draw, Show { .... }

とした方がより近いですかね。

# わざとJavaっぽく書いてますが、西川さんの言う「Javaっぽくなってしまった」
ってこういう感じなのでしょうか??


 で、JavaでのPointクラスを拡張してRectangle(長方形)クラスを考える場合も

  class Rectangle implements Draw, Show { .... }

だと思って、
------------------------------
-- 長方形
data Rectangle = Rt { hx, hy, lx, ly :: Int }  -- 左上の点と右下の点

instance Draw Rectangle where
  move dx dy Rt {hx=a, hy=b, lx=c, ly=d} = Rt (a+dx) (b+dy) (c+dx) (d+dy)

instance Show Rectangle where
  show Rt {hx=a, hy=b, lx=c, ly=d} = "Rt{hx="++(show a)++", hy="++(show b)
                                      ++", lx="++(show c)++",
ly="++(show d)++"}"
------------------------------

と定義すると、こんな感じ↓で実行できます。(hugsで実行)
------------------------------
Main> move 2 3 (Pt 0 5)
Pt{x=2, y=8}
Main> move 2 3 (Rt 0 5 3 2)
Rt{hx=2, hy=8, lx=5, ly=5}
------------------------------

これは、Javaでこんなの↓を実行したのと同じ…かな?
------------------------------
Point pt = new Point (0, 5);
pt.move(2,3);
pt.show();

Rectangle rt = new Rectangle (0, 5, 3, 2);
rt.move(2,3);
rt.show();
------------------------------
一方で、Javaのとは違いますが、関数move の多重定義にもなっています。


 最後に、上の例題のJava版とHaskell版をとりあえず完成させてみたので、はっ
つけます。Haskell版はghcでコンパイルしてください。ツッコミ大歓迎で。


<<Inherit.java>>

interface Draw {
  void move (int dx, int dy);
}
interface Show {
  void show ();
}

// 点
class Point implements Draw, Show {
  private int x, y;
  public Point (int initX, int initY) {
    x = initX; y = initY;
  }
  public void move (int dx, int dy) {
    x += dx;
    y += dy;
  }
  public void show () {
    System.out.println("Pt{x="+x+", y="+y+"}");
  }
}

// 長方形
class Rectangle implements Draw, Show {
  private int x, y, lx, ly;
  public Rectangle (int initX, int initY, int initLX, int initLY) {
    x = initX; y = initY;
    lx = initLX; ly = initLY;
  }
  public void move (int dx, int dy) {
    x += dx; y += dy;
    lx += dx; ly += dy;
  }
  public void show () {
    System.out.println("Rt{x="+x+", y="+y+", lx="+lx+", ly="+ly+"}");
  }
}

public class Inherit {
  public static void main (String[] args) {

    // 点
    Point pt = new Point (0, 5);
    pt.move(2,3);
    pt.show();


    // 長方形
    Rectangle rt = new Rectangle (0, 5, 3, 2);
    rt.move(2,3);
    rt.show();

  }
}


<<inherit.hs>>

-- 図形
class Draw a where
  move :: Int -> Int -> a -> a  -- x,y座標を指定された分だけ移動

-- 点
data Point = Pt { x, y :: Int }

instance Draw Point where
  move dx dy Pt {x=a, y=b} = Pt (a+dx) (b+dy)

instance Show Point where
  show Pt {x=a, y=b} = "Pt{x="++(show a)++", y="++(show b)++"}"

-- 長方形
data Rectangle = Rt { hx, hy, lx, ly :: Int }  -- 左上の点と右下の点

instance Draw Rectangle where
  move dx dy Rt {hx=a, hy=b, lx=c, ly=d} = Rt (a+dx) (b+dy) (c+dx) (d+dy)

instance Show Rectangle where
  show Rt {hx=a, hy=b, lx=c, ly=d} = "Rt{hx="++(show a)++", hy="++(show b)
                                      ++", lx="++(show c)++",
ly="++(show d)++"}"


main = do
	print $ move 2 3 (Pt 0 5)
	print $ move 2 3 (Rt 0 5 3 2)


--
IMAI Takeo <usitukai / osk.3web.ne.jp>

--
ML: haskell-jp / quickml.com
使い方: http://QuickML.com/