Friday, October 19, 2007

在F#语言中创建class

如果有人告诉你,某种语言有超过三种方法定义一个class,你肯定会觉得这是某个O-O狂人设计的超级“编程就是O-O,生活就是O-O”语言,但是他又告诉你,这实际上是一种functional programming语言,你肯定无语了。这种语言的确存在,它就是F#。下面列出三种定义class的方法,以抒发我强烈的感情(不知道是什么感情,就是很强烈)。所有的例子都是定义包括一个property和一个getter,一个setter的类。虽然“真的(O-O)猛士”,是不会写getter/setter的,而那些张口闭口framework,design pattern,却写出一对getter/setter来的人,根本就是没入门的伪O-O,我用这样的例子,只是为了方便。言归正传:

方法一,records of functions with local states:
#light
type EnvModelObj<'T> = { get: unit -> 'T; set: 'T -> unit }

let NewEnvModel initState =
let state = ref initState
{
set = (fun x -> state := x);
get = (fun () -> !state)
}

let o1 = NewEnvModel 1
o1.set 3
print_any (o1.get ())

这是在functional programming language中引入objects的经典方法。SICP中有讲述了用scheme实现这种套路,因此这种方法看上去更fp一些,但是有以下缺点:
  1. 先要定义一个代表这个类的界面的record type,然后定义一个返回这个record的函数,居然和C++定义class的方法十分像!两个定义中包含了重复的信息,违反了DRY原则。
  2. 所有的成员,因为必须使用ref变量,都必须box,效率受损。
  3. 这是一个F#类,其它.NET语言看不懂这个类。
方法二,records with member functions:

type RecordWithMemberFunction =
{ mutable state : int}
with
member this.get () =
this.state
member this.set x =
this.state <- x static member ctor initState =
{ state = initState }
end
let o3 = RecordWithMemberFunction.ctor 5
o3.set 6
print_any (o3.get ())
感觉有点怪异,不理解F#为什么要有这么一个feature,为了实践Perl的Many ways to do one thing哲学吗?

方法三:first-class .NET class
type Class<'T> = class
val mutable state: 'T
new initState as this =
{ state = initState }
then
print_string "initalizing "
print_any this
print_newline ()

member this.get () = this.state
member this.set x =
this.state <- x
end

let o4 = new Class 7
o4.set 8
print_any (o4.get ())
这样定义的class是标准的.NET class,可以给其它语言使用,但是用F#定义这样一个class,也比其它语言省不了多少“笔墨”了。

末了,我要告诉大家,blogger.com把我的代码的缩进搞得一塌糊涂,简直无可救药,再次证明了我的理论:the (x)html web sucks sucks sucks。

No comments: