動機
這已經雪藏很久了,久在github page還沒用到之前就在我的google docs中 整理一下貼出來
Ch1 datatype in Java
abstract class N {}
class Zero extends N {}
class AddOne extends N {}
=>
datatype N = Zero | AddOne of N
就是datatype
The First Bit of Advice
When specifying a collection data, use abstract classes for datatypes and extended classes for variants.
Ch2 list recur ( [type] -> bool & [type(‘a)] -> ‘a)
onlyA
abstract class Ab {
abstract boolean onlyA();
}
class Base extends Ab {
boolean onlyA() { return true; }
}
class B extends Ab {
Ab ab;
B(Ab _ab) { ab = _ab; }
boolean onlyA() { return false; }
}
class A extends Ab {
Ab ab;
A(Ab _ab) { ab = _ab; }
boolean onlyA() { return ab.onlyA(); }
}
ab.{{func}}
,在ML中的ab是{{func}}的參數!!
datatype Ab = Base | A of Ab | B of Ab
fun onlyA(Base) = true
| onlyA(A(ab)) = onlyA(ab)
| onlyA(B(ab)) = false
(onlyA : Ab -> bool)
hold_what
abstract class Ab {
abstract boolean onlyA();
abstract Object hold_what();
}
class Base extends Ab {
Object hold;
Base(Object _hold) { hold = _hold; }
Object hold_what() { return hold; }
boolean onlyA() { return true; }
}
class B extends Ab {
Ab ab;
B(Ab _ab) { ab = _ab; }
boolean onlyA() { return false; }
Object hold_what() { return ab.hold_what; }
}
class A extends Ab {
Ab ab;
A(Ab _ab) { ab = _ab; }
boolean onlyA() { return ab.onlyA(); }
Object hold_what() { return ab.hold_what; }
}
datatype ‘a Ab = Base of ‘a | A of Ab | B of Ab
fun hold_what(Base(x)) = x
| hold_what(A(ab)) = hold_what(ab)
| hold_what(B(ab)) = hold_what(ab)
(hold_what : ‘a Ab -> ‘a)
The Second Bit of Advice
When writing a function over a datatype, place a method in each of the variants that make up the datatype. If a field of a variant belongs to the same datatype, the method may call the corresponding method of the field in computing the function.
template method pattern
abstract class Point {
abstract boolean closerTo(Point p);
abstract int dist();
}
class C extends Point {
int x,y;
C(int x,int y) { x = x; y= y;}
boolean colserTo(Point p) { return dist() <= p.dist(); }
int dist() { return abs(sqrt(x*x+y*y)); }
}
class M extends Point {
int x,y;
C(int x,int y) { x = x; y= y;}
boolean colserTo(Point p) { return dist() <= p.dist(); }
int dist() { return x+y; }
}
可以把相同的抽到abstract放著
abstract class Point {
boolean closerTo(Point p) { return dist() <= p.dist(); }
abstract int dist(); // template,template method pattern
}
my tip
在所有variant都一樣的function,可以放到abstract class
Ch3 list recur ([type] -> [type])
remA
abstract class Ab {
abstract Ab remA();
}
class Base extends Ab {
Ab remA() { return new Base(); }
}
class B extends Ab {
Ab ab;
B(Ab _ab) { ab = _ab; }
Ab remA() { return new B(ab.remA()); }
}
class A extends Ab {
Ab ab;
A(Ab _ab) { ab = _ab; }
Ab remA() { return ab.remA(); }
}
datatype Ab = Base | A of Ab | B of Ab
fun remA(Base) = Base
| remA(A(ab)) = remA(ab)
| remA(B(ab)) = B(remA(ab))
(remA : Ab -> Ab)
appB
abstract class Ab {
abstract Ab remA();
abstract Ab appB();
}
class Base extends Ab {
Ab remA() { return new Base(); }
Ab appB() { return new Base(); }
}
class B extends Ab {
Ab ab;
B(Ab _ab) { ab = _ab; }
Ab remA() { return new B(ab.remA()); }
Ab appB() { return new B(ab.appB()); }
}
class A extends Ab {
Ab ab;
A(Ab _ab) { ab = _ab; }
Ab remA() { return ab.remA(); }
Ab appB() { return new B(new A(ab.appB())); }
}
(這還需要翻成ML嗎)
如果 appB().remA()
,其實與replace A成B是一樣的所以
repA
abstract class Ab {
abstract Ab repA();
}
class Base extends Ab {
Ab repA() { return new Base(); }
}
class B extends Ab {
Ab ab;
B(Ab _ab) { ab = _ab; }
Ab repA() { return new B(ab.repA()); }
}
class A extends Ab {
Ab ab;
A(Ab _ab) { ab = _ab; }
Ab repA() { return new B(ab.repA()); }
}
The Third Bit of Advice
When writing a function that returns values of a datatype, use new to create these values.
composite & interpreter pattern
abstract class PizzaD {
abstract PizzaD remA();
abstract PizzaD topAwC();
abstract PizzaD subAbC();
}
class Crust extends PizzaD {
PizzaD subAbC(){
return new Crust();
}
PizzaD topAwC(){
return new Crust();
}
PizzaD subAbC(){
return new Crust();
}
}
class Cheese extends PizzaD {
PizzaD p;
Cheese (PizzaD _p) {
p = _p;
}
PizzaD remA(){
return new Cheese(p.remA());
}
PizzaD topAwC(){
return new Cheese(p.topAwC());
}
PizzaD subAbC(){
return new Cheese(p.subAbC());
}
}
class Olive extends PizzaD {
PizzaD p;
Olive (PizzaD _p) {
p = _p;
}
PizzaD remA(){
return new Olive(p.remA());
}
PizzaD topAwC(){
return new Olive(p.topAwC());
}
PizzaD subAbC(){
return new Olive(p.subAbC());
}
}
class Anchovy extends PizzaD {
PizzaD p;
Anchovy (PizzaD _p) {
p = _p;
}
PizzaD remA(){
return p.remA();
}
PizzaD topAwC(){
return new Cheese(new Anchovy(p.topAwC()));
}
PizzaD subAbC(){
return new Cheese(p.subAbC());
}
}
class Sausage extends PizzaD {
PizzaD p;
Sausage (PizzaD _p) {
p = _p;
}
PizzaD remA(){
return new Sausage(p.remA());
}
PizzaD topAwC(){
return new Sausage(p.topAwC());
}
PizzaD subAbC(){
return new Sausage(p.subAbC());
}
}
這兩個pattern的本質是都是自己嵌套自己的datatype,所以可以達成如同list效果,可以無限嵌套來組合
Ch4 pattern match ver1(pass all args by myself & func in abstract class)
從Java到SML?
先複習onlyA
abstract class Ab {
abstract boolean onlyA();
}
class Base extends Ab {
boolean onlyA() { return true; }
}
class B extends Ab {
Ab ab;
B(Ab _ab) { ab = _ab; }
boolean onlyA() { return false; }
}
class A extends Ab {
Ab ab;
A(Ab _ab) { ab = _ab; }
boolean onlyA() { return ab.onlyA(); }
}
接著改寫成像ML的樣子,把所有動作都放到同一個地方
class OnlyA { // closure!! OR visitor
boolean forBase() { return true; }
boolean forA(Ab ab) { return ab.onlyA(); }
boolean forB(Ab ab) { return false; }
}
datatype Ab = Base | A of Ab | B of Ab
fun onlyA(Base) = true
| onlyA(A(ab)) = onlyA(ab)
| onlyA(B(ab)) = false
(onlyA : Ab -> bool)
這下把執行動作從variant分離出來了.
但這要怎麼用?? 如果這closure固定在一個所有variant可以碰到的地方的話
visitor (first try)
abstract class Ab {
OnlyA func = new OnlyA();
abstract boolean onlyA();
}
class Base extends Ab {
boolean onlyA() { return func.forBase(); }
}
class B extends Ab {
Ab ab;
B(Ab _ab) { ab = _ab; }
boolean onlyA() { return func.forB(ab); }
}
class A extends Ab {
Ab ab;
A(Ab _ab) { ab = _ab; }
boolean onlyA() { return func.forA(ab); }
}
The Fourth Bit of Advice
When writing several functions for the same self-referential datatype, use visitor protocols so that all methods for a function can be found in a single class.
Ch5 equal!?(match self-defined type) & object as type var
原本應該要用equals去幹一個,但我懶所以用Integer
class Rem1 {
IntList forNull() { return new Null(); }
IntList forCons(IntList l, Object i) {
if(1.equals(i))
return l.rem1();
else
return new Cons(i,l.rem1());
}
}
abstract class IntList {
Rem1 func = new Rem1();
abstract IntList rem1();
}
class Null extends IntList {
IntList rem1() { return func.forNull(); }
}
class Cons extends IntList {
Integer i;
IntList next;
// …..
IntList rem1() { return func.forCons(next, i); }
}
這裡省略了一個地方,因為我這裡是用integer所以不用override euqals,但是書上是自訂的class(variant),所以要用equals要自己實作,而相等的條件就是同類的class的實體,所以用instanceof.
SubstV
class SubstV {
PieD forBot(Object n, Object o) {
return new Bot();
}
PieD forTop (Object t, PieD r, Object n, Object o) {
if (o.equals(t))
return new Top(n, r.subst(n, o));
else
return new Top(t, r.subst(n, 0));
}
}
abstract class PieD {
RemV remFn = new RemV();
SubsbV substFn = new SubstV();
abstract PieD rem(Object o);
abstract PieD subst(Object n, Object o);
}
class Bot extends PieD {
PieD rem(Object o){
return remFn.forBot(o);
}
PieD subst(Object n, Object o){
return substFn.forBot(n, o)
}
}
class Top extends PieD {
Object t; // type var
PieD r;
Top(Object _t, PieD _r){
t = _t;
r = _r;
}
PieD rem(Object o){
return remFn.forTop(t, r, o);
}
PieD subst(Object n, Object o){
return substFn.forTop(t, r, n, o)
}
}
這是不是與list很像,不過注意到subst的Object n, Object o在遞迴的時候其實不會變,所以應該要把它抽出來,scheme是letrec或curry,ML是curry,那這邊要怎麼做?
Ch6 pattern match ver2 (pass closure to invoker & curry func)
class SubstV {
Object n,o;
// ….
PieD forBot() {
return new Bot();
}
PieD forTop (Object t, PieD r) {
if (o.equals(t))
return new Top(n, r.subst(this));
else
return new Top(t, r.subst(this));
}
}
如果照原本的把closure固定在一個所有variant可以碰到的地方的方式,我們沒機會使用我們自己new出來的clousre!!
從upper class傳closure變成從參數傳
那要做出一個空間出來!!
abstract class PieD {
abstract PieD subst(SubstV func);
}
class Bot extends PieD {
PieD subst(SubstV func){
return func.forBot();
}
}
class Top extends PieD {
Object t;
PieD r;
Top(Object _t, PieD _r){
t = _t;
r = _r;
}
PieD subst(SubstV func){
return func.forTop(t,r);
}
}
每個closure都是有for…,for…的話可以拉上去變成abstract嗎? 可以!! 直接看像是function template,但是當成function type就是PieD -> PieD!!
所以這其實代表的是function type,與ml越來越像了
interface PieI {
PieD forBot();
PieD forTop(Object t,PieD p);
}
(subst : ‘a Pie -> ‘a Pie)
都抽成interface了,invoke的地方要不要統一一下
假設我們有許多closure,那每個都要給一個function name在variant的class,等著丟PieI的實體嗎? 可以設一個invoke(在書上是accept)接收closure,再把需要的參數灌進去.
abstract class PieD {
abstract PieD invoke(PieI func);
}
class Bot extends PieD {
PieD invoke(PieI func){
return func.forBot();
}
}
class Top extends PieD {
Object t;
PieD r;
Top(Object _t, PieD _r){
t = _t;
r = _r;
}
PieD invoke(PieI func){
return func.forTop(t,r);
}
}
class SubstV {
Object n,o;
// ….
PieD forBot() {
return new Bot();
}
PieD forTop (Object t, PieD r) {
if (o.equals(t))
return new Top(n, r.invoke(this));
else
return new Top(t, r.invoke(this));
}
}
如果clousre有狀態?
如果是有acc(有狀態的)的函數
class LtdSubstV implements PieVisitorI {
int c;
Object n;
Object o;
LtdSubstV(int _c, Object _n, Object _o) {
c = _c;
n = _n;
o = _o;
}
public PieD forBot() {
return new Bot();
}
public PieD forTop(Object t, PieD r) {
if (c == 0)
return new Top(t, r);
else
if (o.equals(t))
return new Top(n, r.accept(this); //這裡的狀態應該改變
else
return new Top(t, r.accept(this));
}
}
那就new一下,畢竟這裡是functional programming
class LtdSubstV implements PieVisitorI {
int c;
Object n;
Object o;
LtdSubstV(int _c, Object _n, Object _o) {
c = _c;
n = _n;
o = _o;
}
public PieD forBot() {
return new Bot();
}
public PieD forTop(Object t, PieD r) {
if (c == 0)
return new Top(t, r);
else
if (o.equals(t))
return new Top(n, r.accept(LtdSubstV(c-1, n, o)));
else
return new Top(t, r.accept(this));
}
}
The sixth Bit of Advice
When the additional consumed values change for a self-referenced use of a visitor, don’t forget to create a new visitor.
Ch7 tree recur & object as type var
tree走訪
abstract class TreeD {
abstract boolean accept(bTreeVisitorI ask);
abstract int accept(iTreeVisitorI ask);
abstract TreeD accept(tTreeVisitorI ask);
}
class Bud extends TreeD {
boolean accept(bTreeVisitorI ask) {
return ask.forBud();
}
int accept(iTreeVisitorI ask) {
return ask.forBud();
}
TreeD accept(tTreeVisitorI ask) {
return ask.forBud();
}
}
class Flat extends TreeD {
FruitD f;
TreeD t;
Flat(FruitD _f, TreeD _t) {
f = _f;
t = _t;
}
boolean accept(bTreeVisitorI ask) {
return ask.forFlat(f, t);
}
int accept(iTreeVisitorI ask) {
return ask.forFlat(f, t);
}
TreeD accept(tTreeVisitorI ask) {
return ask.forFlat(f, t);
}
}
class Split extends TreeD {
TreeD l;
TreeD r;
Split(Treed _l, TreeD _r) {
l = _l;
r = _r;
}
boolean accept(bTreeVisitorI ask) {
return ask.forSplit(l, r);
}
int accept(iTreeVisitorI ask) {
return ask.forSplit(l, r);
}
TreeD accept(tTreeVisitorI ask) {
return ask.forFlat(l, r);
}
}
不想每個return type都寫一個interface (那個時候應該還沒有泛型)
把不同回傳type的值合併起來
interface TreeVisitorI {
Object forBud();
Object forFlat(FruitD f, TreeD t);
Object forSplit(TreeD l, TreeD r);
}
abstract class TreeD {
abstract Object accept(TreeVisitorI ask);
}
class IsFlatV implements TreeVisitorI {
public Object forBud() {
return new Boolean(true);
}
public Object forFlat(FruitD f, TreeD t) {
return t.accept(this);
}
public Object forSplit(TreeD l, TreeD r) {
return new Boolean(false);
}
}
class IsSplitV implements bTreeVisitorI {
public boolean forBud() {
return new Boolean(true);
}
public boolean forFlat(FruitD f, TreeD t) {
return new Boolean(false);
}
public boolean forSplit(TreeD l, TreeD r) {
if (((Boolean)(l.accept(this))).booleanValue())
return r.accept(this);
else
return new Boolean(false);
}
}
因為我們目前沒有範型,用範型才比較正常
The seventh bit of advice
When designing visitor protocols for many different types, create a unifying protocol using Object.
Ch8 arith(abstract actions) & 2 types of inherence
到處都是cast
interface ExprVisitorI {
Object forPlus(ExprD l, ExprD r); // 相加
Object forDiff(ExprD l, ExprD r); // 相减
Object forProd(ExprD l, ExprD r); // 相乘
Object forConst(Object c); // 常量
}
class IntEvalV implements ExprVisitorI {
public Object forPlus(ExprD l, ExprD r){
return plus(l.accept(this), r.accept(this));
}
public Object forDiff(ExprD l, ExprD r){
return diff(l.accept(this), r.accept(this));
}
public Object forProd(ExprD l, ExprD r){
return prod(l.accept(this), r.accept(this));
}
public Object forConst(Object c){
return c;
}
Object plus(Object l, Object r){
return new Integer(((Integer)l).intValue()+((Integer)r).intValue());
}
Object diff(Object l, Object r){
return new Integer(((Integer)l).intValue()-((Integer)r).intValue());
}
Object prod(Object l, Object r){
return new Integer(((Integer)l).intValue()*((Integer)r).intValue());
}
}
為了避免大量的轉換(其實就是隱藏plus, diff, prod的實作),所以把general action拉出來.
再抽象一次
假設這裡要一個對set操作的eval,那可以
- 直接cast用method,簡單暴力
class SetEvalV extends IntEvalV {
Object plus(Object l, Object r){
return ((SetD)l).plus((SetD)r);
}
Object diff(Object l, Object r){
return ((SetD)l).diff((SetD)r);
}
Object prod(Object l, Object r){
return ((SetD)l).prod((SetD)r);
}
}
- 重新拉一個class出來
abstract class EvalD implements ExprVisitorI {
public Object forPlus(ExprD l, ExprD r){
return plus(l.accept(this), r.accept(this));
}
public Object forDiff(ExprD l, ExprD r){
return diff(l.accept(this), r.accept(this));
}
public Object forProd(ExprD l, ExprD r){
return prod(l.accept(this), r.accept(this));
}
public Object forConst(Object c){
return c;
}
abstract Object plus(Object l, Object r);
abstract Object diff(Object l, Object r);
abstract Object prod(Object l, Object r);
}
class IntEvalV extends EvalD {
Object plus(Object l, Object r){
return new Integer(((Integer)l).intValue()+((Integer)r).intValue());
}
Object diff(Object l, Object r){
return new Integer(((Integer)l).intValue()-((Integer)r).intValue());
}
Object prod(Object l, Object r){
return new Integer(((Integer)l).intValue()*((Integer)r).intValue());
}
}
class SetEvalV extends EvalD {
Object plus(Object l, Object r){
return ((SetD)l).plus((SetD)r);
}
Object diff(Object l, Object r){
return ((SetD)l).diff((SetD)r);
}
Object prod(Object l, Object r){
return ((SetD)l).prod((SetD)r);
}
}
The eighth bits of advice
When extending a class, use overriding to enrich its functionality.
繼承有兩種,一個是繼承自通用class來做出自己想要的功能,這是理想case;而第二種是複寫原有功能的繼承,雖然說這不合理,然而現實中常常看不到別人的class,只能這樣來使用.
Ch9 extend visitor
用繼承增加支援的type
當要為datatype用那種比較討厭的繼承時來加variant時,visitor要怎麼調整
datatype shape = Union of Shape * Shape | …
func f = …
func newF(Union(a,b)) = ….
| newF(x) = f(x)
class Union extends ShapeD {
ShapeD s;
ShapeD t;
Union(ShapeD _s, ShapeD _t) {
s = _s;
t = _t;
}
boolean accept(ShapeVisitorI ask) { // 這裡是implement原本的abstract
((UnionVisitorI)ask).forUnion(s, t); //要自己轉,不然看不到多加的part
}
}
interface UnionVisitorI extends ShapeVisitorI { // interface 可以 extends !!!
boolean forUnion(ShapeD s, ShapeD t);
}
class UnionHasPtV extends HasPtV implements ShapeVisitorI {
UnionHasPtV(PointD _p) {
super(_p);
}
public boolean forUnion(ShapeD s, ShapeD t) {
return s.accept(this) || t.accept(this);
}
}
抽象constructor
這行Trans(CartP(3,7),Union(Square(10),Circle(10))).invoke(UnionV)
陪下面的code,會出事
class HasPtV implements ShapeVisitorI {
PointD p;
HasPtV(PointD _p) {
p = _p;
}
public boolean forCircle(int r) {
return p.distanceTo0() <= r;
}
public boolean forSquare(int s) {
return p.x <= s && p.y <= s;
}
public boolean forTrans(PointD q, ShapeD s) {
return s.accept(new HasPtV(p.minus(q))); // 兇手
}
}
因為Trans會call到上一層的visitor,之後就回不來UnionHasPtV了!!
所以要有辦法讓它能夠產生新的,抽象constrctor
class HasPtV implements ShapeVisitorI {
PointD p;
HasPtV(PointD _p) {
p = _p;
}
ShapeVisitorI newHasPt(PointD p) { // factory method
return new HasPtV(p);
}
public boolean forCircle(int r) {
return p.distanceTo0() <= r;
}
public boolean forSquare(int s) {
return p.x <= s && p.y <= s;
}
public boolean forTrans(PointD q, ShapeD s) {
return s.accept(newHasPtV(p.minus(q)));
}
}
The nineth bit of advice
If a datatype may have to be extended, be forward looking and use a constructor-like(override) method so that visitors can be extended too.
factory method pattern
class UnionHasPtV extends HasPtV implements UnionVisitorI {
UnionHasPtV(PointD _p) {
super(_p);
}
ShapeVisitorI newHasPt(PointD p) { // factory method
return new UnionHasPtV(p);
}
public boolean forUnion(ShapeD s, ShapeD t) {
return s.accept(this) || t.accept(this);
}
}
Ch10 Visitor pattern(obj作為打包好的參數&state[直接修改obj])
觀察在class的invoke,都只是把field的東西傳到closure中而已,那object其實可以直接access自己的field,那可以直接傳obj?
interface PieVisitorI {
Object forBot(Bot that);
Object forTop(Top that);
}
abstract class PieD {
abstract Object accept(PieVisitorI ask);
}
class Bot extends PieD {
Object accept(PieVisitorI ask) {
return ask.forBot(this);
}
}
class Top extends PieD {
Object t;
PieD r;
Top(Object _t, Object _r) {
t = _t;
r = _r;
}
Object accept(PieVisitorI ask) {
return ask.forTop(this);
}
}
class OccursV implements PieVisitorI {
Object a;
OccursV(Object _a) {
a = _a;
}
public Object forBot(Bot that) {
return new Integer(0);
}
public Object forTop(Top that) {
if (that.t.equals(a))
return new Integer(((Integer)(that.r.accept(this))).intValue()+1);
else
return that.r.accept(this);
}
}
現在是直接傳物件,那能不能直接改,這樣就不用new了
class SubstV implements PieVisitorI {
Object n;
Object o;
SubstV(Object _n, Object _o) {
n = _n;
o = _o;
}
public Object forBot(Bot that) {
return new Bot();
}
public Object forTop(Top that) {
if (o.equals(that.t))
return new Top(n, (PieD)(that.r).accept(this));
else
return new Top(that.t, (PieD)(that.r).accept(this));
}
}
class SubstV implements PieVisitorI {
Object n;
Object o;
SubstV(Object _n, Object _o) {
n = _n;
o = _o;
}
public Object forBot(Bot that) {
return that;
}
public Object forTop(Top that) {
if (o.equals(that.t))
that.t = n; //直接被改完了,同時r也沒變,不用設回去
that.r.accept(this);
return that;
}
}
the tenth bit of advice
When modifications to objects are needed, use a class to insulate the operations that modify objects. Otherwise, beware the consequences of your actions.
Y in Java
datatype 'a T = Into of 'a T -> 'a
fun Y(f)= H(f)(Into(H(f)))
and H(f)(a) = f(G(a))
and G(Into(a))(x) = a(Into(a))(x);
class Mk {
Func apply(Func f) {
return new Fact(f);
}
}
class Fact {
Func f;
Fact(Func _f) {
f = _f;
}
Integer apply(Integer x) {
if (x == 0) return 1;
else return x * f.apply(x - 1);
}
}
class Y {
Func apply(Mk f) {
H h = new H(f);
return h.apply(h);
}
}
class H {
Mk f;
H(Mk _f) {
f = _f;
}
H apply(H a) {
return f.apply(new G(a));
}
}
class G {
H h;
G(H _h) {
h = _h;
}
Integer apply(Integer x) {
return h.apply(h).apply(x);
}
}