如何在 F# 中为仿函数在 OCaml 中的作用编写代码?
How to write code in F# for what functors do in OCaml?
我有许多用 OCaml 编写的程序,其中一些程序使用仿函数。现在,我正在考虑用 F# 编写和重写部分代码(以利用 OCaml 没有的一些优势)。我害怕的一件事是在 F# 中为仿函数在 OCaml 中所做的事情编写代码。
例如,我们如何在 F# 中模拟 this example from OCaml manual?
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
如您所见,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
这里有一个有点不同的方法,它使用通用 class 和每个类型一个对象来实现相同的结果。
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]
F# 中的函数式方式主要依赖于类型推断,避免像 interface
这样的 OOP 结构或具有 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
并不是真正必要的,但它们有助于掩盖实际实现(如果需要的话)。
我有许多用 OCaml 编写的程序,其中一些程序使用仿函数。现在,我正在考虑用 F# 编写和重写部分代码(以利用 OCaml 没有的一些优势)。我害怕的一件事是在 F# 中为仿函数在 OCaml 中所做的事情编写代码。
例如,我们如何在 F# 中模拟 this example from OCaml manual?
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
如您所见,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
这里有一个有点不同的方法,它使用通用 class 和每个类型一个对象来实现相同的结果。
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]
F# 中的函数式方式主要依赖于类型推断,避免像 interface
这样的 OOP 结构或具有 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
并不是真正必要的,但它们有助于掩盖实际实现(如果需要的话)。