How to write code in F# for what functors do in OCaml?

后端 未结 3 2276
一个人的身影
一个人的身影 2021-02-19 19:21

I have many programs written in OCaml, some of them use functors. Now, I am considering of writing and re-writing a part of code in F# (to benefit some advantages that OCaml doe

相关标签:
3条回答
  • 2021-02-19 20:09

    Here is a bit different approach that achieves same outcome using a generic class and one object per type.

    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]
    
    0 讨论(0)
  • 2021-02-19 20:22

    The functional way in F# would rely mostly on type inference avoiding OOP structures like interface or types with 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
    

    And it is used like this:

    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]
    

    The type alias type OrderedSet<'t> = 't list and the functions empty and values are not really necessary but they help to mask the actual implementation (in case that is desirable).

    0 讨论(0)
  • 2021-02-19 20:23

    As you noticed, F# doesn't have functors - F# modules cannot be parameterized by types. You can get similar results in F# using the object oriented parts of the language - interfaces, generic classes and inheritance.

    Here's a heavy handed approach at emulating your example.

    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
    
    0 讨论(0)
提交回复
热议问题