簡體   English   中英

標准ML仿函數示例

[英]Standard ML functor examples

標准ML中的函數與模塊系統相關,並且可以基於其他結構生成結構。 下面給出了為各種類型的列表生成列表組合器的仿函數的示例,但是此示例存在一個問題:

各種類型的列表都具有優勢 - 例如,惰性列表可以無限長,並且concantenation列表具有O(1)concat運算符。 但是當所有這些列表類型符合相同的簽名時,仿函數只能使用它們的常規屬性。

因此,我的問題是:什么是一個很好的例子,當函子有用時,各種生成的結構不會失去它們的特殊能力?

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)提供類型作為結果的一部分(特別是抽象類型),以及(4)提供一整套操作。

請注意,您的示例不使用(3),這可能是仿函數最有趣的方面。 例如,想象一下,實現一個抽象矩陣類型,您希望通過它所基於的矢量類型進行參數化。

ML仿函數的一個特定特征 - 以及核心語言多態函數 - 是它們是參數化的 Parametricity是一種語義屬性,表示(多態代碼)的評估對於它實例化的具體類型是遺忘的。 這是一個重要的屬性,因為它意味着各種語義上的善。 特別是,它提供了非常強大的抽象和推理原則(參見例如Wadler的“免費定理” ,或者我在回答另一個問題時給出的簡短解釋)。 它也是類型擦除編譯的基礎(即,在運行時不需要類型)。

參數化意味着單個仿函數不能針對不同類型實現不同的實現 - 這似乎就是您所要求的。 但是,當然,您可以自由編寫多個函數,這些函數對其參數進行不同的語義/復雜性假設。

希望能回答你的問題。

以下是SML仿函數的一些有用示例。 它們是在以下前提下制作的:如果你可以做一套事情,這可以讓你做另一套事情。

集合的仿函數:如果可以比較元素,則可以使用平衡數據結構(例如二叉搜索樹或其他種類的樹)創建集合。

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方言,這種限制可能會有所放松。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM