ch0: 執行環境

想用wsl來跑sml的話,poly/ml是不錯的選項

ch1: 定義type

datatype num = ZERO | ONE_MORE of num
datatype ‘a Slist = NIL of ‘a | SCONS of ‘a Slist

ch2: 寫function

datatype Abc = A | B of Abc | C of Abc

fun only_B(A) = true
    | only_B(B(x)) = only_B(x)
    | only_B(C(x)) = false
(only_B : Abc -> bool)

datatype ‘a Xyz = X of ‘a | Y of ‘a Xyz | Z of ‘a Xyz
fun is_xy(X(x)) = true
    | is_xy(Y(x)) = is_xy(x)
    | is_xy(Z(x)) = false
(is_xy : ‘a Xyz -> bool)

ch3: 在list上遞迴

fun rem_B(A) = A
    | rem_B(B(x)) = rem_B(x)
    | rem_B(C(x)) = C(rem_B(x))
fun C_in_fron_of_B(A) = A
    | C_in_fron_of_B(B(x)) = C(B(C_in_fron_of_B(x)))
    | C_in_fron_of_B(C(x)) = C(C_in_fron_of_B(x))

fun subst_B_C(x) = rem_B(C_in_fron_of_B(x))
(* OR *)
fun subst_B_C(A) = A
    | subst_B_C(B(x)) = C(subst_B_C(x))
    | subst_B_C(C(x)) = C(subst_B_C(x))
(* 都是走訪同一list故可以用同一種走訪,把兩個式子結合起來(map fusion) *)

ch4: tuple & 多參數function

(A,X,A) :: (Abc * Xyz * Abc)

datatype Abc = A | B | C

fun add_a(A) = (A,A)
    | add_a(B) = (B,A)
    | add_a = (C,A)
(add_a : Abc -> (Abc * Abc))

可以利用type variable來打少一點字

fun add_a(x) = (x * A)
(add_a : ‘a -> (‘a * Abc))

這個比較抽象,但是比較詳細的(照著datatype產生的fun)可以找出更多可能的錯誤

fun has_a(a:Xyz, A, b:Xyz) = true
    | has_a(a:Xyz, b, b:Xyz)) = false
(has_a : (Xyz * Abc * Xyz) -> bool)

ch5: type的參數可以不只一個

type的參數可以不只一個

datatype ‘a De = D | E of (‘a * (‘a De))

datatype Abc = A | B | C

fun rem_a(D) = D
    | rem_a(E(A,x)) = rem_a(x)
    | rem_a(E(B,x)) = E(B,rem_a(x))
    | rem_a(E(C,x)) = E(C,rem_a(x))

(* shorter version *)
fun rem_a(D) = D
    | rem_a(E(A,x)) = rem_a(x)
    | rem_a(E(y,x)) = E(y,rem_a(x))

抽象與組合數

輸入的維度上升,pattern的數量(組合數)上升

fun rem(x,D) = D
     | rem(A,E(A,y)) = rem(A, y)
     | rem(A,E(x,y)) = E(x,rem(A, y))
     | rem(B,E(A,y)) = rem(B, y)
     | rem(B,E(x,y)) = E(x,rem(B, y))
     | rem(C,E(C,y)) = rem(C, y)
     | rem(C,E(x,y)) = E(x,rem(C, y))
(rem : (Abc * Abc De) -> Abc De)

把其中一個維度抽出來,像

fun eq_Abc(A,A) = true
     | eq_Abc(B,B) = true
     | eq_Abc(C,C) = true
     | eq_Abc(x,y) = false
fun rem(x,D)         = D
     | rem(x,E(y,z)) = if eq_Abc(x,y)
                       then rem(x,z)
                       else E(y,rem(x,z))

ch6: 樹形遞迴

datatype tree = E | L of Abc * tree | S of tree * tree
datatype ‘a tree = E | F of ‘a * ‘a tree | S of ‘a tree * ‘a tree

fun occur_Abc(a,E) = 0
    | occur_Abc(a,L(b,x)) = if eq_Abc(a,b) then 1+occur_Abc(a,x) else occur_Abc(a,x)
    | occur_Abc(a,S(x,y)) = occur_Abc(a,x) + occur_Abc(a,y)

sexp & slist

datatype ‘a SList = NIL | SCONS of ‘a Sexp * ‘a SList
and
	       ‘a Sexp = ATOM of ‘a | SLIST of ‘a SList
fun occur_Abc_SL(a,Nil) = 0
    | occur_Abc_SL(a,SCons(x,y)) = occur_abc_SE(a,x) + occur_abc_SL(a,y)
    and
fun occur_Abc_SE(a,Atom(b)) = if eq_Abc(a,b) then 1 else 0
    | occur_Abc_SE(a,Slist(x)) = occur_Abc_SL(a,x)

先展開occur_Abc_SE

fun occur_Abc_SL(a,Nil) = 0
    | occur_Abc_SL(a,SCons(Atom(x),y)) = if eq_Abc(a,b) then 1 + occur_abc_SL(a,y) else 0 + occur_abc_SL(a,y)
    | occur_Abc_SL(a,SCons(Slist(x),y)) = occur_abc_SL(a,x) + occur_abc_SL(a,y)

其實SCons的部分都長的很像,做 抽象與合併 !!

fun eq_Abc_atom(a,Atom(b)) = eq_Abc(a,b)
    | eq_Abc_atom(a,Slist(b)) = false
fun occur_Abc_SL(a,Nil) = 0
    | occur_Abc_SL(a,SCons(x,y)) = if eq_Abc_atom(a,x) then 1+occur_abc_SL(a,y) else occur_Abc_SE(a,x) + occur_abc_SL(a,y) 
    and
fun occur_Abc_SE(a,Atom(b)) = 0
    | occur_Abc_SE(a,Slist(x)) = occur_Abc_SL(a,x)

ch7: first-order function & stream

first-order function & type inference

fun true_mk(x) = true
(true_mk : ‘a -> bool)
datatype B_or_I = HOT of bool | COLD of int

fun help(f)
     = Hot(true_mk(
	if true_mk(__)
	then f
	else true_mk))
(help : ? -> B_or_I)

f的type是什麼? then part與else part的type要一樣,故f的type是’a -> bool

stream

datatype chain = LINK of (int * (int -> chain))

這裡是把function包上datatype,這樣可以把遞迴藏起來,因為是datatype的關係,在使用時一定要先打開來,才可以做下一個動作。

fun ints(n) = LINK(n+1, ints)
fun skips(n) = LINK(n+2, skips)
fun some_ints(n) = if is_mod_5_or_7(n+1) then LINK(n+1, some_ints) else some_ints(n+1)

上面是產生stream的起點,接著要get stream的值

fun chain_item(n,Link(x,f)) = if eq_1(n) then x else chain_item(n-1,f(n))

如果有個is_prime

fun primes(n) = if is_prime(n+1) then LINK(n+1, primes) else primes(n+1)

fib 是一定要的

fun fibs(n)(m) = LINK(n+m, fibs(m))
(fibs : int -> (int -> chain))
fun fibs_1(m) = LINK(1 + m, fibs(m)) ( == fibs(1))

ch8: curried

fun in_range_c(s,l)(x) = if less_then(s,x) then less_than(x,l) else false
(in_range_c : (int * int) -> (int -> bool))
fun combine(Nil,Nil) = Nil
    | combine(Nil,Cons(a,x)) = Cons(a,x)
    | combine(Cons(a,x),Nil) = Cons(a,x)
    | combine(Cons(a,x)),Cons(b,y)) = Cons(a,combine(x,Cons(b,y))

fun combine(Nil,y) = y
    | combine(Cons(a,x)),y) = Cons(a,combine(x,y))

第一種寫法把所有組合列出來 而第二種是只處理一個維度,剩下遞迴

而curried後

fun combine_c(Nil)(y) = y
    | combine_c(Cons(a,x))(y) = Cons(a,combine_c(x)(y))

可以想像成

combine_c(Cons(1,Cons(2,Nil))) => \y -> Cons(1,combine_c(Cons(2,Nil))(y))

所以curried的另一種寫法

fun combine_s(Nil) = fn l2 => l2
    | combine_s(Cons(a,x)) = fn l2 => Cons(a, combine_s(x)(l2))

ch9: exception

datatype box = Bacon | lx of Int
exception No_bacon of int

fun where_is(Nil) = raise No_bacon(0)
    | where_is(Cons(a,x)) = if is_bacon(a) then 1 else 1+where_is(x)

(* where_is(..) handle No_bacon(n) => n *)
exception Out_of_range
fun list_item(n,Nil) = raise Out_of_range
    | list_item(n,Cons(b,rest)) = if eq_int(n,1) then b else list_item(n-1,rest)

fun find(n, boxes)
    = (check(n,boxes,list_item(n,boxes)) handle Out_of_range => find(n div 2, boxes))
and
      check(n,boxes,Bacon) = n
    | check(n,boxes,lx(i)) = find(i,boxes)
fun path(n, boxes)
    = Cons(n,(check(n,boxes,list_item(n,boxes)) handle Out_of_range => path(n div 2, boxes)))
and
      check(n,boxes,Bacon) = n
    | check(n,boxes,lx(i)) = path(i,boxes)

這裡的重點是 [X.. (A.. handle _ => B)],當excepption發生時被接住時,此時會變成[X.. B]。

ch10: module system in ML

(* interface *)
signature N = 
sig
    type number
    exception Too_small
    val succ : number -> number
    val pred : number -> number
    val is_zero : number -> bool
end;

(* class *)
functor NumberAsNum() (* 傳入functor需要的參數(structure)與一些constraint(對內的type暴露) *)
    :> (* implements *)
    N
=
    struct (private and implement signatrue)
    datatype num = Zero | One_more_than of num
    type number = num
    exception Too_small
    fun succ(n) = One_more_than(n)
    fun pred(Zero) = raise Too_small | pred(One_more_than(n)) = n
    fun is_zero(Zero) = true | is_zero(foo) = false;
 end;

(* object *)
structure NumStruct = NumberAsNum();

另一個plus

signature P =
sig
    type number
    val plus : number*number -> number
end;

functor PON(structure a_N : N)
    :>
    P
=
struct
    type number = a_N.number
    fun plus(n,m) = if a_N.is_zero(n) then m else a_N.succ(plus(a_N.pred(n),m))
end;

structure NumArith = PON(structure a_N = NumStruct);

哪兩個可以一起用嗎?

NumArith.plus(Zero, One_more_than(Zero));

我們不知道NumArith的number是什麼!!

要處理這個問題有三個方法

  1. 加入box與unbox的function
signature N_C_R =
sig
    type number
    exception Too_small
    val conceal : int -> number
    val succ : number -> number
    val pred : number -> number
    val is_zero : number -> bool
    val reveal : number -> int
end;

(* NumStruct.reveal( NumArith.plus( NumStruct.conceal(1), NumStruct.conceal(2))); *)
  1. 用where把type暴露出來
functor PON(structure a_N : N)
:>
P where type number = a_N.number
=
struct
    type number = a_N.number
    fun plus(n,m) = if a_N.is_zero(n) then m else a_N.succ(plus(a_N.pred(n),m))
end;

functor NumberAsInt2()
:>
N where type number = int (* 這邊的int是refer到外面的env,不會碰到struct .. end 中的東西,*)
=
struct
    type number = int
    exception Too_small
    fun succ(n) = n + 1 fun pred(n) = if n=0 then raise Too_small else n-1
    fun is_zero(n) = n=0
end;
  1. sharing type標出兩個type要一樣
signature J =
sig
    val new_plus : int*int -> int
end;

functor NP(structure a_N : N_C_R
           structure a_P : P
           sharing type a_N.number = a_P.number)
    :>
    J
=
struct
    fun new_plus(x,y) = a_N.reveal( a_P.plus( a_N.conceal(x), a_N.conceal(y)))
end;

structure NPStruct = NP(structure a_N = NumberAsNum()
    structure a_P = PON(structure a_N = a_N));

小總結

functor <<name>>(args with some relations(A.b is equal to B.c, 這也是為什麼fuctor的參數要named的理由)) 
    :>
    <<sig(return type) with some relations(this type is same as ...)>>
=
struct
    code
end;

Y in ML

Y的重點是有一個(g g)這是無限遞迴的,所以要中斷他。 在第七章的stream(chain)中就利用datatype來打斷無限遞迴,所以這裡也要用相同的技巧。 與chain不同的是,我們的函數的參數是自己,所以不用像chain要多一格來存apply到函數的參數。 所以只要存自己就夠了

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);

fun mk_fact(fact)(n)
    = if n=0
        then 1
        else n*fact(n-1);