[SML] module

1. 背景

Standard ML 语言由1983提案(proposed),经过1984年至1988年进行设计(designed),
规范《The Definition of Standard ML》,最终在1990年完成(defined)。

Standard ML '97Standard ML 的简化版,
修订版规范《The Definition of Standard ML (Revised)》于1997年完成。

修订版的 Standard ML '97 有时候也称为 Standard ML,或者 Standard ML '97SML '97
为了区分,1990年的 Standard ML 也称为 SML '90

2. 模块语言

Standard ML 程序可以划分为独立的单元(unit),程序单元也称为结构(structure)。
结构中包含了一些类型(type)和值(value),
借助签名(signature)可以将多个结构组合成更大的结构,
因此,较大的结构可以按层级划分成不同的子结构(substructure)。

其中,签名可以看做是结构自身的类型(type),
泛型化或参数化的结构,也被称为函子(functor)。

3. 签名和结构

A signature may be thought of as a description of a structure, and a structure may correspondingly be thought of as an implementation of a signature.

签名可以看做是结构的规范描述,结构可以看做是签名的具体实现。
其他语言中也有类似的概念,签名通常被称为接口,或包的规范(package specifications),
结构通常被称为实现(implementation)或包(package)。

而与其他语言不同的是,Standard ML 中的签名和结构之间是多对多关系。
一个签名可以描述多个不同的结构,一个结构可以同时满足多个不同的签名。

3.1 签名

A signature is a specification, or a description, of a program unit, or structure.

签名是一段程序单元(或结构)的规范,或描述。

结构由类型构造器,异常构造器,值的绑定关系构成,
而签名则指定了结构的约束条件(requirement),例如,
结构应当包含哪些类型,包含哪些值,这些值的类型是什么,等等。

一个结构如果满足了这些约束条件(requirement),
我们就说它匹配了(match)或实现了(implement)该签名。

signature QUEUE =
    sig 
        type ’a queue 
        exception Empty 
        val empty : ’a queue 
        val insert : ’a * ’a queue -> ’a queue 
        val remove : ’a queue -> ’a * ’a queue 
    end

以上代码定义了一个名为QUEUE的签名。
匹配该签名的结构,必须满足以下条件:
(1)有一个单参类型构造器,'a queue
(2)有一个零参异常,Empty
(3)有一个值empty,它是多态类型的'a queue
(4)有两个多态函数,insertremove

3.2 签名的继承(inheritance)

通过包含(signature inclusion)和特化(signature specialization),
我们可以使用现有的签名,得到另一个新的签名,
这是继承(inheritance)签名的两种形式。

(1)包含
包含用于得到,比已有签名具有更多内容的签名。

signature QUEUE_WITH_EMPTY =    
    sig 
        include QUEUE 
        val is empty : ’a queue -> bool 
    end

(2)特化
特化用于得到,比已有签名中的类型更具体的签名。

signature QUEUE_AS_LISTS = 
    QUEUE where type ’a queue = ’a list * ’a list

3.3 结构

结构由类型构造器,异常构造器,值的绑定关系构成。

Structures are implementations of signatures; signatures are the types of structures.

结构实现了签名,签名是结构的类型。

structure Queue =
    struct 
        type ’a queue = ’a list * ’a list 
        exception Empty 
        val empty = (nil, nil) 
        fun insert (x, (b,f)) = (x::b, f) 
        fun remove (nil, nil) = raise Empty 
            | remove (bs, nil) = remove (nil, rev bs) 
            | remove (bs, f::fs) = (f, (bs, fs)) 
    end

以上代码定义一个名为Queue的结构,它实现了签名QUEUE

3.4 匹配(mathing)

我们说一个解构实现了某个签名,指的是,
该结构满足签名中所要求的一切类型定义。

结构中提供的类型构造器,必须与签名中所要求的具有相同数目的参数,
结构中提供的值,必须满足签名中所要求的类型,
结构中提供的异常,其参数类型也必须与签名中所要求的一样。

我们把结构所能满足的最严格(stringent),最精确的(precise)签名,称为它的主签名(principal signature)。
我们说一个结构精确(exactly)匹配(match)到了一个签名上,
如果该签名没有比主签名提供更多的约束条件。

如果签名sigexp_1,具有sigexp_2中所有的约束条件,
我们就说,签名sigexp_1可以匹配签名sigexp_2

signature QUEUE = 
    sig 
        type ’a queue 
        exception Empty 
        val empty : ’a queue 
        val insert : ’a * ’a queue -> ’a queue 
        val remove : ’a queue -> ’a * ’a queue 
    end
signature QUEUE_WITH_EMPTY = 
    sig 
        include QUEUE 
        val is empty : ’a queue -> bool 
    end
signature QUEUE_AS_LISTS = 
    QUEUE where type ’a queue = ’a list * ’a list

其中,签名QUEUE_WITH_EMPTYQUEUE_AS_LISTS都可以匹配到QUEUE上。

3.5 归属(ascription)

签名归属(signature ascription)将一个结构强行指定到一个签名上面,
从而限制了以后该结构的被使用的灵活度。

Standard ML中有两种方式对结构进行归属,
(1)透明或描述性归属(transparent, or descriptive ascription)

structure strid : sigexp = strexp

(2)不透明或限制性的归属(opaque, or restrictive ascription)

structure strid :> sigexp = strexp

透明归属使用冒号:,不透明归属使用:>

这两种归属方式中,确定结构strid签名的步骤如下,
(1)验证strexp是否实现了sigexp
我们通过strexp的主签名sigexp_0,是否可以匹配到sigexp上来确定。

(2)在匹配过程中,主签名sigexp_0中,可能包含了比sigexp中更多的类型,
于是,我们将得到一个sigexp的增强版sigexp'

(3)对于透明归属,strid的签名为增强版sigexp'
而对于不透明归属,strid的签名为原版sigexp

例子,
(1)不透明归属

signature QUEUE = 
    sig 
        type ’a queue 
        exception Empty 
        val empty : ’a queue 
        val insert : ’a * ’a queue -> ’a queue 
        val remove : ’a queue -> ’a * ’a queue 
    end
structure Queue :> QUEUE =
    struct 
        type ’a queue = ’a list * ’a list 
        val empty = (nil, nil) 
        fun insert (x, (bs, fs)) = (x::bs, fs) 
        exception Empty 
        fun remove (nil, nil) = raise Empty 
            | remove (bs, f::fs) = (f, (bs, fs)) 
            | remove (bs, nil) = remove (nil, rev bs) 
    end

不透明归属Queue :> QUEUE保证了类型'a queue的抽象性,
Queue可以更改具体实现,而不会影响用户代码,
用户不用关心'a queue的具体类型。

(2)透明归属
透明归属简化了在签名中显式指定所有类型的工作。
我们总是可以将透明归属,替换成不透明归属,再手动扩充一些类型。

signature ORDERED =
    sig 
        type t 
        val lt : t * t -> bool 
    end
structure String : ORDERED =
    struct 
        type t = string 
        val clt = Char.< 
        fun lt (s, t) = ... clt ... 
    end

其中,辅助函数clt对外是不可见的,
虽然ORDERED签名中没有指定,String.t的类型也为string
透明归属,根据ORDERED计算出来了一个增强版的签名,包含了这个类型定义,
所以,String的真实签名是,

ORDERED where type t = string

4. 子结构

一个子结构就是“结构中的结构”,

signature MY_GEN_DICT =
    sig 
        type key 
        type ’a dict 
        val empty : ’a dict 
        val insert : ’a dict * key * ’a -> ’a dict 
    end
signature MY_STRING_DICT = 
    MY_GEN_DICT where type key = string 
    
signature MY_INT_DICT =
    MY_GEN_DICT where type key = int

其中,MY_GEN_DICT是一个一般化的签名,类型key被抽象出来。
而在签名MY_STRING_DICTMY_INT_DICT中,我们可以指定具体的key

除了可以指定签名中具体的类型,还可以指定签名中的具体结构。

signature ORDERED =
    sig 
        type t 
        val lt : t * t -> bool 
        val eq : t * t -> bool 
    end
signature DICT =
    sig 
        structure Key : ORDERED 
        type ’a dict 
        val empty : ’a dict 
        val insert : ’a dict * Key.t * ’a -> ’a dict 
        val lookup : ’a dict * Key.t -> ’a option 
    end
signature STRING_DICT = 
    DICT where type Key.t=string

signature INT_DICT =
    DICT where type Key.t=int

以上代码,先定义了一个名为ORDERED的签名,
然后在签名DICT中,指定其子结构Key满足签名ORDERED
最后,签名STRING_DICTINT_DICT,通过指定子结构Key中类型t的具体值完成实例化。

4. 参数化

为了复用,支持定义泛型化或参数化的模块十分重要,
模块的实现中,留下一部分未完全确定(unspecified)的部分,然后再用不同的方式实例化(instantiated),
那些共同部分,就只需要实现了一次,被所有实例所共享了。

在Standard ML中,这些一般化模块称为函子(functor),
函子是一个模块级别的(module-level)函数,它接受一个结构(structure)作为参数,
然后生成一个结构作为结果。

signature ORDERED =
    sig 
        type t 
        val lt : t * t -> bool 
        val eq : t * t -> bool 
    end
signature DICT =
    sig 
        structure Key : ORDERED 
        type ’a dict 
        val empty : ’a dict 
        val insert : ’a dict * Key.t * ’a -> ’a dict 
        val lookup : ’a dict * Key.t -> ’a option 
    end
functor DictFun 
    (structure K : ORDERED) :> 
        DICT where type Key.t = K.t = 
struct 
    structure Key : ORDERED = K 
    datatype ’a dict = 
        Empty | 
        Node of ’a dict * Key.t * ’a * ’a dict 
    val empty = Empty 
    fun insert (None, k, v) = 
        Node (Empty, k, v, Empty) 
    fun lookup (Empty, ) = NONE 
        | lookup (Node (dl, l, v, dr), k) = 
            if Key.lt(k, l) then 
                lookup (dl, k) 
            else if Key.lt (l, k) then 
                lookup (dr, k) 
            else 
                v
end

DictFun是一个函子,它接受一个结构K作为参数,返回了一个结构。
其中K透明归属于ORDEREDDictFun不透明归属于DICT
它保证了'a dict的抽象性(不透明归属),而K.t则是具体的(透明归属),由函子的参数决定。

函子的调用方式如下,

structure LtIntDict = DictFun (structure K = LessInt) 
structure LexStringDict = DictFun (structure K = LexString) 
structure DivIntDict = DictFun (structure K = DivInt)

参考

Standard ML '97
Programming in Standard ML

你可能感兴趣的:([SML] module)