标准机器学习中的函子与模块系统相关,可以基于其他结构生成结构。下面给出了一个函子为各种类型的列表生成列表组合器的示例,但是这个示例有一个问题:
各种类型的列表都有优点——例如,惰性列表可以无限长,串联列表具有 O(1) 的串联运算符。但是当所有这些列表类型都符合相同的签名时,函子只能使用它们的通用属性。
因此,我的问题是:什么是函子有用且生成的各种结构不会失去其特殊能力的好例子?
signature MYLIST =
sig
type 'a t
val null : 'a t -> bool
val empty : 'a t
val cons : 'a * 'a t -> 'a t
val hd : 'a t -> 'a
val tl : 'a t -> 'a t
end
structure RegularList : MYLIST =
struct
type 'a t = 'a list
val null = List.null
val empty = []
val cons = op::
val hd = List.hd
val tl = List.tl
end
structure LazyList : MYLIST =
struct
datatype 'a t = Nil | Cons of 'a * (unit -> 'a t)
val empty = Nil
fun null Nil = true
| null _ = false
fun cons (x, xs) = Cons (x, fn () => xs)
fun hd Nil = raise Empty
| hd (Cons (x, _)) = x
fun tl Nil = raise Empty
| tl (Cons (_, f)) = f ()
end
structure ConcatList : MYLIST =
struct
datatype 'a t = Nil | Singleton of 'a | Concat of 'a t * 'a t
val empty = Nil
fun null Nil = true
| null (Singleton _) = false
| null (Concat (xs, ys)) = null xs andalso null ys
fun cons (x, xs) = Concat (Singleton x, xs)
fun hd Nil = raise Empty
| hd (Singleton x) = x
| hd (Concat (xs, ys)) = hd xs
fun tl Nil = raise Empty
| tl (Singleton x) = Nil
| tl (Concat (xs, ys)) = (* exercise *)
end
signature MYLISTCOMB =
sig
type 'a t
val length : 'a liste -> int
val map : ('a -> 'b) -> 'a liste -> 'b liste
val foldl : ('a * 'b -> 'b) -> 'b -> 'a liste -> 'b
val append : 'a liste * 'a liste -> 'a liste
val concat : 'a liste liste -> 'a liste
val sort : ('a * 'a -> order) -> 'a t -> 'a t
end
functor ListComb (X : MYLIST) : MYLISTCOMB =
struct
type 'a t = 'a X.t
open X
fun length xs =
if null xs then 0
else 1 + length (tl xs)
fun map f xs =
if null xs then empty
else cons(f (hd xs), map f (tl xs))
fun foldl f e xs =
if null xs then e
else foldl f (f (hd xs, e)) (tl xs)
fun append (xs, ys) =
if null xs then ys
else cons (hd xs, append (tl xs, ys))
fun concat xs =
if null xs then empty
else append (hd xs, concat (tl xs))
fun sort cmp xs = (* exercise *)
end
structure RegularListComb = ListComb (RegularList)
structure LazyListComb = ListComb (LazyList)
structure ConcatListComb = ListComb (ConcatList)
不确定我完全理解你的问题。显然,函子对于定义模块化抽象非常有用,这些模块化抽象(1)是多态的,(2)需要对其类型参数进行一整套操作,以及(3)提供类型作为其结果的一部分(特别是abstract类型) ,(4)提供了一整套操作。
请注意,您的示例没有使用 (3),这可能是函子最有趣的方面。例如,想象一下,实现一个抽象矩阵类型,您想要对其所基于的向量类型进行参数化。
机器学习函子以及核心语言多态函数的一个具体特征是它们是“参数化”的。参数化是一种语义属性,表示(多态代码的)求值忽略了它实例化的具体类型。这是一个重要的属性,因为它意味着各种语义上的优点。特别是,它提供了非常强大的抽象和推理原则(例如,参见 Wadler 的“定理是免费的!”,或者我在回复另一个问题中给出的简短解释)。它也是类型擦除编译的基础(即运行时不需要类型)。 参数化意味着
single函子不能对不同类型有不同的实现——这似乎就是你所问的。但是当然,您可以自由编写多个函子,对它们的参数做出不同的语义/复杂性假设。 希望这能回答您的问题。
如果可以比较元素,则可以使用平衡数据结构(例如二叉搜索树或其他类型的树)创建集合。
signature SET =
sig
type elem
type set
val empty : set
val singleton : elem -> set
val union : set -> set -> set
val intersect : set -> set -> set
end
signature ORD =
sig
type t
val compare : t * t -> order
end
functor BalancedSetFunctor(structure Cmp : ORD) :> SET =
struct
type elem = Cmp.t
type set = ...
val empty = ...
fun singleton x = ...
fun union s1 s2 = ...
fun intersect s1 s2 = ...
end
对于任何类型的事物集合(例如列表),如果你可以迭代它们,你就可以自动折叠它们。您还可以为同一数据类型的不同方式创建不同的结构(例如树的前序、中序和后序遍历)。
signature ITERABLE =
sig
type elem
type collection
val next : collection -> (elem * collection) option
end
signature FOLD =
sig
type elem
type collection
val fold : (elem * 'b -> 'b) -> 'b -> collection -> 'b
end
functor FoldFunctor(Iter : ITERABLE) :> FOLD =
struct
type elem = Iter.elem
type collection = Iter.collection
fun fold f e xs =
case Iter.next xs of
NONE => e
| SOME (x, xs') => fold f (f (x, e)) xs'
end
(这个动词是标准的 FP 术语):对于给定的一组类型和值,它们允许您在它们之上创建一组新的类型和值。 所有符合所需模块接口的模块都可以从仿函数中“受益”,但它们不会失去其特殊能力,如果你所说的能力指的是实现特定的优势。 例如,您的例子很好地证明了我的观点:正如您所写,串联列表有一个非常快的
concat
运算符,并且当使用函子提升时,这种“能力”不会消失。它仍然在那里,甚至可能被函子代码使用。因此,在这个例子中,函子代码实际上受益于列表实现,
而不知道它。这是一个非常强大的概念。 另一方面,由于模块在被函子提升时必须适合接口,因此多余的值和类型会在这个过程中丢失,这可能很烦人。不过,根据 ML 方言,此限制可能会有所放松。