如何用F#编写代码,以说明函子在OCaml中的作用?
我有许多用OCaml编写的程序,其中一些使用函子。现在,我正在考虑用F#编写和重新编写部分代码(以受益于OCaml所没有的一些优势)。我害怕的一件事是用F#编写代码来描述函子在OCaml中的作用 例如,我们如何在F#中模仿如何用F#编写代码,以说明函子在OCaml中的作用?,f#,ocaml,functor,F#,Ocaml,Functor,我有许多用OCaml编写的程序,其中一些使用函子。现在,我正在考虑用F#编写和重新编写部分代码(以受益于OCaml所没有的一些优势)。我害怕的一件事是用F#编写代码来描述函子在OCaml中的作用 例如,我们如何在F#中模仿 类型比较=较小|相等|较大 订购的模块类型\u类型=sig t型 val比较:t->t->比较 结束 模块集= 函子(Elt:ORDERED_TYPE)->struct 类型元素=Elt.t 类型集合=元素列表 设为空=[] 让rec添加x s= 匹配 []->[x] |hd
类型比较=较小|相等|较大
订购的模块类型\u类型=sig
t型
val比较:t->t->比较
结束
模块集=
函子(Elt:ORDERED_TYPE)->struct
类型元素=Elt.t
类型集合=元素列表
设为空=[]
让rec添加x s=
匹配
[]->[x]
|hd::tl->
匹配Elt。将x hd与
相等->s(*x已在s*中)
|小于->x::s(*x小于s*的所有元素)
|更大->高清::添加x tl
结束
模块OrderedString=struct
类型t=字符串
让我们比较x y=如果x=y,则等于,如果x
正如您所注意到的,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
类型比较=较小|相等|较大
///与有序_类型签名对应的接口
类型IOrderedType>(coll:IOrderedType这里有一种稍微不同的方法,它使用泛型类和每个类型一个对象来实现相同的结果
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]
类型比较=较小|相等|较大
输入Set“a->比较)=
成员this.Empty:'a list=[]
此成员。添加x s=
匹配
|[]->[x]
|hd::tl->
将x hd与
|相等->s(*x已在s*中)
|小于->x::s(*x小于s*的所有元素)
|更大->hd::this.Add x tl
让我们比较x y=如果x=y,则等于,如果x[“foo”]
让try2()=intSet.add2 intSet.Empty/->[2]
让try3()=floatSet.Add 3.0 floatSet.Empty/->[3.0]
F#中的功能方式主要依赖于类型推断,避免像接口
或带有成员
的类型这样的OOP结构
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
类型比较=较小|相等|较大
类型OrderedSet[x]
|hd::tl->
将x hd与
|相等->s(*x已在s*中)
|小于->x::s(*x小于s*的所有元素)
|更大->高清::添加tl
地址s
让我们比较x y=如果x=y,则等于,如果x
它是这样使用的:
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]
let try1()=OrderedSet.addGeneric“foo”OrderedSet.empty |>OrderedSet.addGeneric“bar”
设try2()=OrderedSet.addGeneric 2 OrderedSet.empty |>OrderedSet.addGeneric 3
让try3()=OrderedSet.empty
|>OrderedSet.addFloat 3.0
|>OrderedSet.addFloat 1.0
|>OrderedSet.addFloat 2.0
try1()|>printfn“%A”//OrderedSet=[“bar”;“foo”]
try2()|>printfn“%A”//OrderedSet=[2;3]
try3()|>printfn“%A”//OrderedSet=[1.0;2.0;3.0]
类型别名类型OrderedSet感谢您提供此详细答案。。。当你说“重拳出击”时,你的意思是人们在实践中不会这样编码?@SoftTimur:有点-我尽量不偏离原始的编码,当我通过设置时,我感觉不自然。离这里不远,但如果我从一张干净的纸开始,我可能会得到一些不同的结果。@SoftTimur:也许那只是集合
上复杂的类型参数。好吧。。。让我用另一种方式问。。。如果我们假设函子在OCaml中非常自然和常见,那么这种接口+泛型类+继承的组合在F#中是自然的吗?或者人们在F#中以其他方式编写代码来实现类似的事情?@SoftTimur:一般来说-是的,这是进行类型抽象的一种自然方式。这就是我称我的解决方案为“笨手笨脚”而不是“感觉自然”时的意思。如果我提到,并悄悄地、仔细地补充道:当解决方案可用时,为什么要编写代码?;-)(是的,我确实理解边做边学的好处…@Helgereneuroholm问题是如何移植使用函子表示F#的OCaml代码,而不是如何实现排序集。我希望这个例子有助于找出如何将真实世界的案例移植到F#。hvester是的,我确实理解这一点,而且你的答案非常好。但我不能让自己摆脱这样的想法:当移植时,使用您的移植到的任何环境。我想可能是我,而不是密码,答案或其他。。。
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]