代码之家  ›  专栏  ›  技术社区  ›  Pierre-olivier Gendraud

在具体的例子中,将类型模块和函子从ocaml替换为f的最佳方法是什么?[复制品]

  •  0
  • Pierre-olivier Gendraud  · 技术社区  · 6 年前

    我有许多用ocaml编写的程序,其中一些使用了函子。现在,我正在考虑用f_编写和重新编写一部分代码(以获得ocaml所没有的一些优势)。我害怕的一件事是用f来编写代码,因为函数在ocaml中的作用是什么。

    例如,我们如何模仿 this example from OCaml manual 在F?

    type comparison = Less | Equal | Greater
    
    module type ORDERED_TYPE = sig
      type t
      val compare: t -> t -> comparison
    end
    
    module Set =
    functor (Elt: ORDERED_TYPE) -> struct
        type element = Elt.t
        type set = element list
        let empty = []
        let rec add x s =
          match s with
            [] -> [x]
          | hd::tl ->
             match Elt.compare x hd with
               Equal   -> s         (* x is already in s *)
             | Less    -> x :: s    (* x is smaller than all elements of s *)
             | Greater -> hd :: add x tl
      end
    
    module OrderedString = struct
      type t = string
      let compare x y = if x = y then Equal else if x < y then Less else Greater
    end
    
    module OrderedInt = struct
      type t = int
      let compare x y = if x = y then Equal else if x < y then Less else Greater
    end
    
    module StringSet = Set(OrderedString)
    module IntSet = Set(OrderedInt)
    
    let try1 () = StringSet.add "foo" StringSet.empty
    let try2 () = IntSet.add 2 IntSet.empty
    
    0 回复  |  直到 8 年前
        1
  •  8
  •   scrwtp    8 年前

    正如您所注意到的,f没有函子-f模块不能由类型参数化。使用语言的面向对象部分(接口、泛型类和继承),可以在f*中得到类似的结果。

    这里有一个严厉的方法来模仿你的例子。

    type Comparison = Less | Equal | Greater
    
    /// Interface corresponding to ORDERED_TYPE signature
    type IOrderedType<'a> = 
        abstract Value: 'a
        abstract Compare: IOrderedType<'a> -> Comparison
    
    /// Type that implements ORDERED_TYPE signature, different instantiations
    /// of this type correspond to your OrderedInt/OrderedString modules.
    /// The 't: comparison constraint comes from the fact that (<) operator 
    /// is used in the body of Compare.
    type Ordered<'t when 't: comparison> (t: 't) =
        interface IOrderedType<'t> with
            member this.Value = t
            member this.Compare (other: IOrderedType<'t>) = 
                if t = other.Value then Equal else if t < other.Value then Less else Greater
    
    /// A generic type that works over instances of IOrderedType interface.
    type Set<'t, 'ot when 't: comparison and 'ot :> IOrderedType<'t>> (coll: IOrderedType<'t> list) =
    
        member this.Values = 
            coll |> List.map (fun x -> x.Value)
    
        member this.Add(x: 't) = 
            let rec add (x: IOrderedType<'t>) s = 
                match coll with
                | [] -> [x]
                | hd::tl ->
                    match x.Compare(hd) with
                    | Equal   -> s         (* x is already in s *)
                    | Less    -> x :: s    (* x is smaller than all elements of s *)
                    | Greater -> hd :: add x tl
            Set<'t, 'ot>(add (Ordered(x)) coll)
    
        static member Empty = Set<'t, 'ot>(List.empty)
    
    /// A helper function for Set.Add. Useful in pipelines. 
    module Set =     
        let add x (s: Set<_,_>) =
           s.Add(x)
    
    /// Type aliases for different instantiations of Set 
    /// (these could have easily been subtypes of Set as well)
    type StringSet = Set<string, Ordered<string>>
    type IntSet = Set<int, Ordered<int>>
    
    let try1 () = Set.add "foo" StringSet.Empty
    let try2 () = Set.add 2 IntSet.Empty
    
    try1().Values
    try2().Values
    
        2
  •  7
  •   hvester    8 年前

    这里有一个稍微不同的方法,它使用一个泛型类和每种类型一个对象来实现相同的结果。

    type Comparison = Less | Equal | Greater
    
    type Set<'a>(compare : 'a -> 'a -> Comparison) =
    
        member this.Empty : 'a list = []
    
        member this.Add x s = 
             match s with
             | [] -> [x]
             | hd::tl ->
                 match compare x hd with
                 | Equal   -> s         (* x is already in s *)
                 | Less    -> x :: s    (* x is smaller than all elements of s *)
                 | Greater -> hd :: this.Add x tl
    
    
    let compare x y = if x = y then Equal else if x < y then Less else Greater
    
    let compareFloats (x : float) (y : float) = if x = y then Equal else if x < y then Less else Greater
    
    // Note that same generic compare function can be used for stringSet and intSet
    // as long as the type parameter is explicitly given
    let stringSet = Set<string>(compare)
    let intSet = Set<int>(compare)
    
    // Type parameter not needed, because compareFloats is not generic
    let floatSet = Set(compareFloats)
    
    let try1 () = stringSet.Add "foo" stringSet.Empty   // -> ["foo"]
    let try2 () = intSet.Add 2 intSet.Empty             // -> [2]
    let try3 () = floatSet.Add 3.0 floatSet.Empty       // -> [3.0]
    
        3
  •  0
  •   AMieres    6 年前

    f中的函数方式主要依赖于类型推断,避免了像 interface 或类型 member .

    type Comparison = Less | Equal | Greater
    type OrderedSet<'t> = 't list     // type alias, not really necessary
    
    module OrderedSet =
        let empty                      : OrderedSet<_> = List.empty  // just an empty list
        let values (s : OrderedSet<_>) : OrderedSet<_> = s           // identity function
        let add compare x (s : OrderedSet<_>)  : OrderedSet<_> =
            let rec addR s =
                match s with
                | [] -> [x]
                | hd::tl ->
                    match compare x hd with
                    | Equal   -> s         (* x is already in s *)
                    | Less    -> x :: s    (* x is smaller than all elements of s *)
                    | Greater -> hd :: addR tl
            addR s
    
        let compare        x          y = if x = y then Equal else if x < y then Less else Greater
        let compareFloats (x : float) y = if x = y then Equal else if x < y then Less else Greater
    
        let addGeneric v = add compare       v
        let addFloat   v = add compareFloats v
    

    它的用法如下:

    let try1 () = OrderedSet.addGeneric "foo" OrderedSet.empty |> OrderedSet.addGeneric "bar"
    let try2 () = OrderedSet.addGeneric 2     OrderedSet.empty |> OrderedSet.addGeneric 3 
    let try3 () = OrderedSet.empty 
                  |> OrderedSet.addFloat 3.0 
                  |> OrderedSet.addFloat 1.0 
                  |> OrderedSet.addFloat 2.0
    
    try1() |> printfn "%A"  // OrderedSet<string> = ["bar"; "foo"]  
    try2() |> printfn "%A"  // OrderedSet<int>    = [2; 3]
    try3() |> printfn "%A"  // OrderedSet<float>  = [1.0; 2.0; 3.0]
    

    类型别名 type OrderedSet<'t> = 't list 以及功能 empty values 不是真正必要的,但它们有助于掩盖实际的实现(如果这是可取的话)。